C++;静态多态性(CRTP)和使用派生类的typedef 我读了C++中奇怪的重复模板模式来做静态(读:编译时)多态性。我想对其进行泛化,以便根据派生类型更改函数的返回类型。(这似乎是可能的,因为基类型从模板参数知道派生类型)。不幸的是,下面的代码无法使用MSVC2010编译(我现在无法轻松访问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; } 模板 阶级基础{ 公众: typedef typename派生的\u t::value\u type value\u type; 值\类型foo(){ 返回static_cast(this)->foo(); } }; 模板 派生类:公共基{ 公众: 类型定义T值_类型; 值\类型foo(){ return T();//返回一些T对象(假设T是默认可构造的) } }; int main(){ 导出了一个; }
顺便说一句,我在使用额外的模板参数方面做了一些工作,但我不喜欢它——在继承链上传递许多类型时,它会变得非常冗长C++;静态多态性(CRTP)和使用派生类的typedef 我读了C++中奇怪的重复模板模式来做静态(读:编译时)多态性。我想对其进行泛化,以便根据派生类型更改函数的返回类型。(这似乎是可能的,因为基类型从模板参数知道派生类型)。不幸的是,下面的代码无法使用MSVC2010编译(我现在无法轻松访问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; } 模板 阶级基础{ 公众: typedef typename派生的\u t::value\u type value\u type; 值\类型foo(){ 返回static_cast(this)->foo(); } }; 模板 派生类:公共基{ 公众: 类型定义T值_类型; 值\类型foo(){ return T();//返回一些T对象(假设T是默认可构造的) } }; int main(){ 导出了一个; },c++,templates,inheritance,typedef,crtp,C++,Templates,Inheritance,Typedef,Crtp,顺便说一句,我在使用额外的模板参数方面做了一些工作,但我不喜欢它——在继承链上传递许多类型时,它会变得非常冗长 template <typename derived_t, typename value_type> class base { ... }; template <typename T> class derived : public base<derived<T>,T> { ... }; 模板 类基{…}; 模板 派生类:公共基{…};
template <typename derived_t, typename value_type>
class base { ... };
template <typename T>
class derived : public base<derived<T>,T> { ... };
模板
类基{…};
模板
派生类:公共基{…};
编辑:
MSVC 2010在这种情况下给出的错误消息是error C2039:“value\u type”:不是“派生”的成员。
g++4.1.2(via)表示
错误:“类派生”中没有名为“value\u type”的类型
派生的
在其基类列表中将其作为模板参数使用时是不完整的
一个常见的解决方法是使用traits类模板。这是你的例子,traitsified。这显示了如何通过traits使用派生类中的类型和函数
// 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();
}
};
//声明基本类模板:
模板
结构碱基特性;
//定义使用特征的基类:
模板
结构基{
typedef typename base_traits::value_type value_type;
值\u类型基本\u foo(){
return base_traits::call_foo(static_cast(this));
}
};
//定义派生类;它也可以使用以下特征:
模板
派生结构:基{
typedef typename base_traits::value_type value_type;
值\u类型派生的\u foo(){
返回值_type();
}
};
//声明并定义派生属性的基本属性专用化:
模板
结构基特征{
类型定义T值_类型;
静态值\u类型调用\u foo(派生*x){
返回x->派生的_foo();
}
};
您只需对用于
base
的模板参数derived\u t
的任何类型的base\u traits
进行专门化,并确保每个专门化提供base
所需的所有成员。使用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;
}
模板
阶级基础{
公众:
类型定义T值_类型;
值\类型foo(){
返回static_cast(this)->foo();
}
};
模板
派生类:公共基{
公众:
类型定义T值_类型;
值\类型foo(){
return T();//返回一些T对象(假设T是默认可构造的)
}
};
int main(){
导出了一个;
}
在C++14中,您可以删除typedef
并使用函数auto
返回类型扣除:
template <typename derived_t>
class base {
public:
auto foo() {
return static_cast<derived_t*>(this)->foo();
}
};
模板
阶级基础{
公众:
自动foo(){
返回static_cast(this)->foo();
}
};
这是因为
base::foo
返回类型的推导被延迟到派生的\u t
完成。需要较少样板文件的类型特征的替代方法是将派生类嵌套在保存typedef(或使用)的包装类中并将包装器作为模板参数传递给基类
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);
}
模板
结构基{
使用派生=类型名外部::派生;
使用value\u type=typename外部::value\u type;
值类型基函数(int x){
返回静态_cast(this)->派生的_func(x);
}
};
//outer保存我们的typedef,derived保存其余的
模板
结构外部{
使用值_type=T;
派生结构:公共基{//outer现在已完成
值\u类型派生的\u func(int x){return 5*x;}
};
};
//如果你愿意,你可以给它取个更好的名字
模板
使用NicerName=typename外部::派生;
int main(){
尼塞尔奈;
返回对象基本函数(5);
}
我知道这基本上是您发现的并且不喜欢的解决方法,但我想记录它,并且说它基本上是这个问题的当前解决方案
我一直在寻找一种方法来做到这一点,但从来没有找到一个好的解决办法。
这是不可能的,这就是为什么像boost::iterator\u facade
这样的东西最终需要很多参数的原因
当然,我们希望这样的事情能够奏效:
template<class CRTP>
struct incrementable{
void operator++(){static_cast<CRTP&>(*this).increment();}
using ptr_type = typename CRTP::value_type*; // doesn't work, A is incomplete
};
template<class T>
struct A : incrementable<A<T>>{
void increment(){}
using value_type = T;
value_type f() const{return value_type{};}
};
int main(){A<double> a; ++a;}
模板
可递增结构{
void操作符++(){static_cast(*this).increment();}
使用ptr_type=typename CRTP::value_type*;//无效,A不完整
};
模板
结构A:可递增
缺点是派生类中的trait必须使用限定的typename
访问,或者使用重新启用 正如您所知,codepad.org可以为您编译和运行代码,我相信它使用的是gcc/g++。因此,您永远不会脱离g++:)您能添加您遇到的错误,以便我对读者有用。@Seth:Ideone肯定使用gcc,所以它是另一个:)@Seth:谢谢您提供有关codepad.org的提示@斯利拉姆:打得好。我添加了它们。我创建了一个测试文件并编译了OP的代码,在Ubuntu g++4.4.1中添加了main(),效果很好。声明一个像Derived
这样的对象会出错,我正在试图找出原因?@iammilind这是因为在一个空的main()
no templat中
template<class CRTP, class ValueType>
struct incrementable{
void operator++(){static_cast<CRTP&>(*this).increment();}
using value_type = ValueType;
using ptr_type = value_type*;
};
template<class T>
struct A : incrementable<A<T>, T>{
void increment(){}
typename A::value_type f() const{return typename A::value_type{};}
// using value_type = typename A::value_type;
// value_type f() const{return value_type{};}
};
int main(){A<double> a; ++a;}