Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to derive from a nested class of a variadic template argument?

Given the following two structs, one could derive from both nested 'Nested' classes, and call foo() and bar() from the derived object:

struct WithNested1 {
    template<class T> struct Nested {
        void foo();
    };
};

struct WithNested2 {
    template<class T> struct Nested {
        void bar();
    };
};

struct Test : WithNested1::Nested<Test>,
              WithNested2::Nested<Test>
{

};

Test test;
test.foo();
test.bar();


But, if both of the outer classes were passed as variadic template arguments, how would you derive from them?

For example, this fails to compile:

template<typename... Ts>
struct Test : Ts::template Nested<Test>...
{

};

Test<WithNested1, WithNested2> test;
test.foo();
test.bar();

error: 'foo' : is not a member of 'Test'
error: 'bar' : is not a member of 'Test'

strangely, it compiles if the calls to foo() and bar() are removed.

like image 452
CuriousGeorge Avatar asked Nov 21 '25 11:11

CuriousGeorge


2 Answers

template <typename... Ts>                                                          
struct Test : Ts::template Nested<Test<Ts...>>...                                  
{                                                                                  

};  

This is the same answer as above but I figured I'd explain how it works. First in your example Test has no template param (which the compiler should warn you of), but which should we give it. The point of CRTP is to give the class you inherit from a template param that is the same as your type, that way it has access to your methods and members through the of the template param. Your type in this case is Test<Ts...> so that is what you have to pass it. As @aschepler already pointed out normally you could use Test by itself but it's not in scope until your already inside the class.

I think this is a cleaner way of doing what you want.

template <typename T>                                                              
struct A {                                                                         
    void bar (){                                                                   
        static_cast<T*>(this)->val = 3;                                            
    }                                                                              
};                                                                                 

template <typename T>                                                              
struct B {                                                                         
    void foo (){                                                                   
        static_cast<T*>(this)->val = 90;                                           
    }                                                                              
};                                                                                 


template <template<class> class ... Ts>                                            
struct Test : Ts<Test<Ts...>>...                                                   
{                                                                                  
    int val;                                                                       
};                                                                                 

int main() {                                                                       
    Test<A,B> test;                                                                
    test.foo();                                                                    
    test.bar();                                                                    
    return 0;                                                                      
}  
like image 69
aaronman Avatar answered Nov 24 '25 00:11

aaronman


The "injected class name" Test which can be used as an abbreviation of Test<Ts...> is not in scope where you tried to use Nested<Test>, since the class scope does not begin until the { token.

Use

template<typename... Ts>
struct Test : public Ts::template Nested<Test<Ts...>>...
{
};
like image 21
aschepler Avatar answered Nov 24 '25 00:11

aschepler



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!