문제

Consider the following example:

#include <iostream>
#include <iostream>
#include <type_traits>

template<typename Type, template<typename> class Crtp>
class Base
{
    public:
        typedef int value;

        // f1: OK
        // Expected result: casts 4.2 to Base<Type, Crtp>::value
        value f1() {return 4.2;}

        // f2: NOT OK
        // Expected result: casts 4.2 to Crtp<Type>::value
        // But f2 does not compile: no type named 'value' 
        // in 'class Derived<double>'
        typename Crtp<Type>::value f2() {return 4.2;} 
};

template<typename Type>
class Derived : public Base<Type, Derived>
{
    public:
        typedef Type value;
};

int main()
{
    Derived<double> a;
    std::cout<<a.f1()<<std::endl;
    std::cout<<a.f2()<<std::endl;
    return 0;
}

How to solve this problem (Derived typedef unknown from the Base class) ?

EDIT: I've found a very simple trick. Can someone explain to me why the following is working and the previous version does not work? Is this trick ok with standard C++11 or it works because of the way the compiler works (here g++ 4.7.1) ?

#include <iostream>
#include <iostream>
#include <type_traits>

template<typename Type, template<typename> class Crtp>
class Base
{
    public:
        typedef int value;
        value f1() {return 4.2;}
        template<typename T = Crtp<Type>> typename T::value f2() {return 4.2;}
};

template<typename Type>
class Derived : public Base<Type, Derived>
{
    public:
        typedef Type value;
};

int main()
{
    Derived<double> a;
    std::cout<<a.f1()<<std::endl;
    std::cout<<a.f2()<<std::endl;
    return 0;
}
도움이 되었습니까?

해결책

You have to use a wrapper class (here it's value_getter) which is declared before you define Base. You can then specialize it right before you defined Derived:

template<typename T>
struct value_getter;

template<typename Type, template<typename> class Crtp>
class Base
{
    public:
        typedef int value;

        value f1() {return 4.2;}

        // in 'class Derived<double>'
        typename value_getter<Crtp<Type> >::value f2() {return 4.2;} 
};

template<typename Type>
class Derived;

template<typename Type>
struct value_getter<Derived<Type> > {
    typedef Type value;
};

template<typename Type>
class Derived : public Base<Type, Derived>, public value_getter<Derived<Type> >
{
    public:
};

It's not exactly pretty, but at least it works.

다른 팁

Your trick works because f2 now doesn't get instantiated until it is actually used, when class Derived has been completed.

In your particular example, I might just recommend doing this:

#include <iostream>
#include <iostream>
#include <type_traits>

template<typename Type, template<typename> class Crtp>
class Base
{
    public:
        typedef int value;

        value f1() {return 4.2;}

        Type f2() {return 4.2;} 
};

template<typename Type>
class Derived : public Base<Type, Derived>
{
    public:
        typedef Type value;
};

int main()
{
    Derived<double> a;
    std::cout<<a.f1()<<std::endl;
    std::cout<<a.f2()<<std::endl;
    return 0;
}

but your real code may have other needs that make that impractical.

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