混合typedef和CRTP?

时间:2012-12-24 14:20:00

标签: c++ templates c++11 metaprogramming crtp

考虑以下示例:

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

如何解决此问题(Derived类中的Base typedef未知?)

编辑:我发现了一个非常简单的技巧。有人可以向我解释为什么以下工作,以前的版本不起作用?使用标准C ++ 11这个技巧是否正常,或者因为编译器的工作方式(这里是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;
}

2 个答案:

答案 0 :(得分:3)

您必须使用在定义value_getter之前声明的包装类(此处为Base)。然后,您可以在定义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:
};

它不完全漂亮,但至少它有效。

答案 1 :(得分:1)

你的诀窍是有效的,因为在实际使用类型Derived时,f2现在不会被实例化。

在您的特定示例中,我可能只是建议这样做:

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

但是您的真实代码可能还有其他需求使其变得不切实际。