【问题标题】:Mixing typedef and CRTP?混合 typedef 和 CRTP?
【发布时间】:2012-12-24 14:19:59
【问题描述】:

考虑以下示例:

#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 typedef 未知来自 Base 类)?

编辑:我发现了一个非常简单的技巧。有人可以向我解释为什么以下内容有效而以前的版本无效吗?这个技巧适用于标准 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;
}

【问题讨论】:

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


    【解决方案1】:

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

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

    【讨论】:

    • 而问题似乎是Crtp&lt;Type&gt;Base的成员函数体之外是不完整的。
    • @Vincent 之所以有效,是因为模板仅在第一次使用时才被实例化,这是在 Base 的定义之后发生的。
    【解决方案2】:

    您的技巧奏效了,因为 f2 现在在实际使用它之前不会被实例化,此时类 Derived 已经完成。

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

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

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

    【讨论】:

    • @Vincent:在您的特定示例中,有一种更简单的方法,但在其他情况下可能不起作用。在编写 C++ 时,您通常只需要使用您能找到的最不难看的解决方案。
    猜你喜欢
    • 2016-07-17
    • 1970-01-01
    • 1970-01-01
    • 2023-02-02
    • 1970-01-01
    • 2011-08-25
    • 1970-01-01
    相关资源
    最近更新 更多