C ++ 11调用基类的模板参数包上的成员函数(如果存在)

时间:2014-10-01 13:20:15

标签: c++ templates c++11

我检查了类似的问题。这很接近,但不重复。

本质上,我想在基类的参数包上调用一个函数(如果存在)。我有一个C ++ 11这样做的方式有效,但我觉得不满意。

有人可以提供更好的[即更好的性能和更少的样板代码]:

源代码:

#include <iostream>
#include <type_traits>

using namespace std;

// a class initialised with an int that can't do it
struct A
{
    A(int a) : _a(a) {  }
    void report() const { std::cout << _a << std::endl; }
private:
    int _a;
};

// a class initialised with a string that can do it
struct B
{
    B(std::string s) : _b (move(s)) {  }
    void report() const { std::cout << _b << std::endl; }
    void do_it() { std::cout << "B did it with " << _b <<"!" << std::endl; }
private:
    string _b;
};

// a class initialised with an int that can do it
struct D
{
    D(int d) : _d(d) {  }
    void report() const { std::cout << _d << std::endl; }
    void do_it() { std::cout << "D did it with " << _d <<"!" << std::endl; }
private:
    int _d;
};

// a class initialised with a string that can't do it
struct E
{
    E(std::string s) : _e(move(s)) { }
    void report() const { std::cout << _e << std::endl; }
private:
    string _e;
};

// a function enabled only if T::do_it is a member function pointer
// the bool is there just to make this function more attractive to the compiler
// than the next one, below
template<class T>
auto do_it(T& t, bool)
-> typename std::enable_if<std::is_member_function_pointer<decltype(&T::do_it)>::value, void>::type
{
    t.do_it();
}

// a catch-all function called when do_it<T> is not valid
// the ... is less attractive to the compiler when do_it<T>(T&, bool) is available
template<class T>
void do_it(T& t, ...)
{
}

// a compound class derived from any number of classes - I am so lazy I work hard at
// being lazy.
template<class...Templates>
struct C : public Templates...
{
    // construct from a parameter pack of arbitrary arguments
    // constructing each base class with one argument from the pack
    template<class...Args>
    C(Args&&...args)
    : Templates(std::forward<Args>(args))...
    {

    }

    // private implementation of the dispatch mechanism here...
private:
    // this will call ::do_it<T>(T&, bool) if T::do_it is a member function of T, otherwise
    // calls ::do_it<T>(T&, ...)
    template<class T>
    void may_do_it()
    {
        ::do_it(static_cast<T&>(*this), true);
    }

    // calls may_do_it for the last class in the parameter pack
    template<typename T1>
    void multi_may_do_it()
    {
        may_do_it<T1>();
    }

    // general case for calling do_it on a parameter pack of base classes
    template<typename T1, typename T2, typename...Rest>
    void multi_may_do_it()
    {
        may_do_it<T1>();
        multi_may_do_it<T2, Rest...>();
    }

    // calls may_do_it for the last class in the parameter pack
    template<typename T1>
    void multi_report() const
    {
        static_cast<const T1&>(*this).report();
    }

    // general case for calling do_it on a parameter pack of base classes
    template<typename T1, typename T2, typename...Rest>
    void multi_report() const
    {
        static_cast<const T1&>(*this).report();
        multi_report<T2, Rest...>();
    }


    // the functions we actually wish to expose here...
public:
    // disptach T::do_it for each valid T in base class list
    void do_it() {
        multi_may_do_it<Templates...>();
    }

    // dispatch T::report, which must exist for each base class
    void report() const {
        cout << "-- all base classes reporting:" << endl;
        multi_report<Templates...>();
        cout << "-- all base classes reported" << endl;
    }
};

int main()
{
    C<A,B, D, E> c(10, "hello", 7, "goodbye");
    c.report(); // all base classes must report
    c.do_it(); // all base classes that can do_it, must.

   return 0;
}

输出:

Compiling the source code....
$g++ -std=c++11 main.cpp -o demo -lm -pthread -lgmpxx -lgmp -lreadline 2>&1

Executing the program....
$demo 
-- all base classes reporting:
10
hello
7
goodbye
-- all base classes reported
B did it with hello!
D did it with 7!

1 个答案:

答案 0 :(得分:4)

我认为这就像你可以做到的那样没有样板。

// a function enabled only if T::do_it is a member function pointer
template<class T>
auto do_it(T* t)
-> typename std::enable_if<std::is_member_function_pointer<decltype(&T::do_it)>::value, void>::type
{
    t->do_it();
}

// a catch-all function called when do_it<T> is not valid
// the const void * is less attractive to the compiler when do_it<T>(T*) is available
template<class T>
void do_it(const void *)
{
}

// a compound class derived from any number of classes - I am so lazy I work hard at
// being lazy.
template<class...Templates>
struct C : public Templates...
{
    //constructor omitted
private:
    using expander = int[];
public:
    // disptach T::do_it for each valid T in base class list
    void do_it() {
        (void) expander{ 0, (::do_it<Templates>(this), 0)...};
    }

    // dispatch T::report, which must exist for each base class
    void report() const {
        cout << "-- all base classes reporting:" << endl;
        (void) expander{ 0, (Templates::report(), 0)...};
        cout << "-- all base classes reported" << endl;
    }
};

Demo