C++静态多态(CRTP)并使用派生类中的typedef

Sam*_*ell 62 c++ inheritance templates typedef crtp

我阅读了维基百科关于C++中用于执行静态(读取:编译时)多态的奇怪重复模板模式的文章.我想概括它,以便我可以根据派生类型更改函数的返回类型.(这似乎应该是可能的,因为基类型知道模板参数中的派生类型).不幸的是,以下代码将无法使用MSVC 2010进行编译(我现在没有轻松访问gcc所以我还没有尝试过).谁知道为什么?

template <typename derived_t>
class base {
public:
    typedef typename derived_t::value_type value_type;
    value_type foo() {
        return static_cast<derived_t*>(this)->foo();
    }
};

template <typename T>
class derived : public base<derived<T> > {
public:
    typedef T value_type;
    value_type foo() {
        return T(); //return some T object (assumes T is default constructable)
    }
};

int main() {
    derived<int> a;
}
Run Code Online (Sandbox Code Playgroud)

顺便说一下,我有一个使用额外模板参数的解决方法,但我不喜欢它 - 当在继承链上传递许多类型时会变得非常冗长.

template <typename derived_t, typename value_type>
class base { ... };

template <typename T>
class derived : public base<derived<T>,T> { ... };
Run Code Online (Sandbox Code Playgroud)

编辑:

MSVC 2010在这种情况下给出的错误消息是 error C2039: 'value_type' : is not a member of 'derived<T>'

g ++ 4.1.2(通过codepad.org)说error: no type named 'value_type' in 'class derived<int>'

Jam*_*lis 65

derived当您base在其基类列表中将其用作模板参数时,它是不完整的.

常见的解决方法是使用traits类模板.这是你的例子,traitsified.这显示了如何通过特征使用派生类中的类型和函数.

// Declare a base_traits traits class template:
template <typename derived_t> 
struct base_traits;

// Define the base class that uses the traits:
template <typename derived_t> 
struct base { 
    typedef typename base_traits<derived_t>::value_type value_type;
    value_type base_foo() {
        return base_traits<derived_t>::call_foo(static_cast<derived_t*>(this));
    }
};

// Define the derived class; it can use the traits too:
template <typename T>
struct derived : base<derived<T> > { 
    typedef typename base_traits<derived>::value_type value_type;

    value_type derived_foo() { 
        return value_type(); 
    }
};

// Declare and define a base_traits specialization for derived:
template <typename T> 
struct base_traits<derived<T> > {
    typedef T value_type;

    static value_type call_foo(derived<T>* x) { 
        return x->derived_foo(); 
    }
};
Run Code Online (Sandbox Code Playgroud)

您只需要专门base_traits用于模板参数derived_t的任何类型,base并确保每个特化提供所需的所有成员base.

  • @iammilind那是因为在一个空的`main()`中没有模板实例化发生.只有在编译器尝试实例化并使用模板时才会出现某些错误. (2认同)
  • 你能解释一下你的意思吗?"当你用它作为基础类列表的模板参数时,派生是不完整的".它是什么方式不完整? (2认同)
  • @JamesMcNellis 和 Sriram,我觉得可能会出现混乱。稍微扁平化的版本,衍生的不是模板:`struct衍生:base &lt;衍生&gt; {...};`确实使base_traits未定义。 (2认同)

mat*_*tch 9

使用traits的一个小缺点是你必须为每个派生类声明一个.您可以编写一个不那么详细和重新划分的解决方法,如下所示:

template <template <typename> class Derived, typename T>
class base {
public:
    typedef T value_type;
    value_type foo() {
        return static_cast<Derived<T>*>(this)->foo();
    }
};

template <typename T>
class Derived : public base<Derived, T> {
public:
    typedef T value_type;
    value_type foo() {
        return T(); //return some T object (assumes T is default constructable)
    }
};

int main() {
    Derived<int> a;
}
Run Code Online (Sandbox Code Playgroud)


Okt*_*ist 6

在C++ 14中,您可以删除typedef并使用函数auto返回类型推导:

template <typename derived_t>
class base {
public:
    auto foo() {
        return static_cast<derived_t*>(this)->foo();
    }
};
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为扣除返回类型base::foo会延迟到derived_t完成.

  • 这种技术会限制返回值吗?它适用于字段或函数参数吗?C ++ 17会提供更多帮助吗? (2认同)