문제

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.

도움이 되었습니까?

해결책

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;                                                                      
}  

다른 팁

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...>>...
{
};

This works:

template<typename... Ts>
struct Test : Ts::template Nested<Test<Ts...>>...
//                                    ^^^^^^^
{
};

9/2:

[...]. The class-name is also inserted into the scope of the class itself; this is known as the injected-class-name. For purposes of access checking, the injected-class-name is treated as if it were a public member name. [...]

14.6.1/1:

Like normal (non-template) classes, class templates have an injected-class-name (Clause 9). The injectedclass-name can be used as a template-name or a type-name. When it is used with a template-argument-list, as a template-argument for a template template-parameter, or as the final identifier in the elaborated-typespecifier of a friend class template declaration, it refers to the class template itself. Otherwise, it is equivalent to the template-name followed by the template-parameters of the class template enclosed in <>.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top