C ++静态多态(CRTP)并使用派生类的typedef
我阅读了维基百科有关在C ++中使用静态(读:编译时)多态性的好奇的循环模板模式的文章 。 我想概括它,以便我可以根据派生types更改函数的返回types。 (这看起来应该是可能的,因为基types从模板参数中知道派生types)。 不幸的是,下面的代码不会使用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; }
顺便说一句,我有一个解决scheme使用额外的模板参数,但我不喜欢它 – 它会变得非常详细时,inheritance链传递多种types。
template <typename derived_t, typename value_type> class base { ... }; template <typename T> class derived : public base<derived<T>,T> { ... };
编辑:
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>'
derived
是不完整的,当你使用它作为模板参数的base
在其基类列表中。
常见的解决方法是使用traits类模板。 这是你的例子,traitsified。 这显示了如何通过特征使用派生类中的types和函数。
// 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(); } };
你只需要将base_traits
专门base_traits
任何用于base
模板参数derived_t
的types,并确保每个特化提供了base
所需的所有成员。
使用特征的一个小缺点是你必须为每个派生类声明一个。 您可以编写一个不太冗长的重新编译的解决方法,如下所示:
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; }
另一种types的特征,需要较less的样板文件是将派生类嵌套在一个包装你的types定义(或使用)的包装类,并将包装作为模板parameter passing给你的基类。
template <typename Outer> struct base { using derived = typename Outer::derived; using value_type = typename Outer::value_type; value_type base_func(int x) { return static_cast<derived *>(this)->derived_func(x); } }; // outer holds our typedefs, derived does the rest template <typename T> struct outer { using value_type = T; struct derived : public base<outer> { // outer is now complete value_type derived_func(int x) { return 5 * x; } }; }; // If you want you can give it a better name template <typename T> using NicerName = typename outer<T>::derived; int main() { NicerName<long long> obj; return obj.base_func(5); }
在C ++ 14中,您可以删除typedef
并使用函数auto
返回types演绎:
template <typename derived_t> class base { public: auto foo() { return static_cast<derived_t*>(this)->foo(); } };
这是有效的,因为base::foo
的返回types的推导延迟到derived_t
完成。
您可以避免在template
传递2个参数。 在CRTP中,如果您有保证class base
将与class derived
(而不是class derived_2
)配对的保证,请使用以下技术:
template <typename T> class derived; // forward declare template <typename value_type, typename derived_t = derived<value_type> > class base { // make class derived as default argument value_type foo(); };
用法:
template <typename T> class derived : public base<T> // directly use <T> for base