TravisG https://stackoverflow.com/users/740202/travisg(原:黑蛇)已经回答了 https://stackoverflow.com/a/6803124/14089,就我而言。
但我想对你的问题发表评论:
所以如果我使用带有 X 的模板,则可以用任何东西填充它
不,因为如果没有可访问的,它就无法编译kewl_method
.
您必须记住,在 Java 中,有界泛型并不是像您所认为的那样限制泛型类接受的类型,而是为泛型类提供有关其泛型类型 T 的更完整的信息,以便能够验证调用在编译时调用它的方法。
在 C++ 中,此功能按原样提供并由编译器在何处使用:以类似于鸭子类型的方式,但在编译时解析,仅当泛型类型类具有访问权限时,编译器才会接受该方法的编译到kewl_method
.
以 4 个类为例:
class X
{
public : virtual void kewl_method() { /* etc. */ }
} ;
class Y : public X
{
public : virtual void kewl_method() { /* etc. */ }
} ;
class Z
{
public : virtual void kewl_method() { /* etc. */ }
} ;
class K
{
public : virtual void wazaa() { /* etc. */ }
} ;
普通 C++ 解决方案
使用 C++ 模板,您可以提供模板化的类 A:
template<typename T>
class A
{
public :
void foo()
{
T t ;
t.kewl_method() ;
}
} ;
...具有 X、Y 和 Z 类,但不具有 K 类,因为:
- X:它实现了
kewl_method()
- Y :它公开派生自 X,它实现了
kewl_method()
- Z:它实现了
kewl_method()
- K:它没有实现
kewl_method()
...这比 Java(或 C#)的泛型强大得多。用户代码为:
int main()
{
// A's constraint is : implements kewl_method
A<X> x ; x.foo() ; // OK: x implements kewl_method
A<Y> y ; y.foo() ; // OK: y derives from X
A<Z> z ; z.foo() ; // OK: z implements kewl_method
A<K> k ; k.foo() ; // NOT OK : K won't compile: /main.cpp error:
// ‘class K’ has no member named ‘kewl_method’
return 0;
}
您需要致电foo()
方法来阻止编译。
如果确实需要约束怎么办?
如果您想将其显式限制为从 X 继承的类,则必须使用代码自行完成(直到C++ 概念 https://secure.wikimedia.org/wikipedia/en/wiki/Concepts_%28C%2B%2B%29已标准化...他们错过了 C++0x 的最后期限,所以我想我们将不得不等待下一个标准...)
If you really want to put constraints, there are multiple ways. While I know about it, I'm not familiar enough with the SFINAE https://secure.wikimedia.org/wikipedia/en/wiki/Substitution_failure_is_not_an_error concept to give you the solution, but I can still see two ways to apply constraints for your case (while they were tested for g++ 4.4.5, could someone wiser validate my code?):
添加未使用的演员表?
B类与A类类似,只是多了一行代码:
template<typename T> // We want T to derive from X
class B
{
public :
void foo()
{
// creates an unused variable, initializing it with a
// cast into the base class X. If T doesn't derive from
// X, the cast will fail at compile time.
// In release mode, it will probably be optimized away
const X * x = static_cast<const T *>(NULL) ;
T t ;
t.kewl_method() ;
}
} ;
当 B::foo() 被调用时,只有在以下情况下才会编译:T*
可以被投射到X*
(这只能通过公共继承来实现)。
结果是:
int main()
{
// B's constraint is : implements kewl_method, and derives from X
B<X> x ; x.foo() ; // OK : x is of type X
B<Y> y ; y.foo() ; // OK : y derives from X
B<Z> z ; z.foo() ; // NOT OK : z won't compile: main.cpp| error:
// cannot convert ‘const Z*’ to ‘const X*’
// in initialization
B<K> k ; k.foo() ; // NOT OK : K won't compile: /main.cpp error:
// cannot convert ‘const K*’ to ‘const X*’
// in initialization
return 0 ;
}
但是,作为 A 示例,您需要调用foo()
方法来阻止编译。
添加一个自产的“约束”和一个类?
让我们创建一个类来表达对其构造函数的约束:
template<typename T, typename T_Base>
class inheritance_constraint
{
public:
inheritance_constraint()
{
const T_Base * t = static_cast<const T *>(NULL) ;
}
} ;
您会注意到该类是空的,并且其构造函数不执行任何操作,因此很有可能它会被优化掉。
您可以像以下示例一样使用它:
template<typename T>
class C : inheritance_constraint<T, X> // we want T to derive from X
{
public :
void foo()
{
T t ;
t.kewl_method() ;
}
} ;
私有继承意味着您的“inheritance_constraint”不会与您的代码发生冲突,但它仍然在编译时表达了一个约束,该约束将停止对不是从 X 派生的类 T 进行编译:
结果是:
int main()
{
// C's constraint is : implements kewl_method, and derives from X
C<X> x ; // OK : x is of type X
C<Y> y ; // OK : y derives from X
C<Z> z ; // NOT OK : z won't compile: main.cpp error:
// cannot convert ‘const Z*’ to ‘const X*’
// in initialization
C<K> k ; // NOT OK : K won't compile: /main.cpp error:
// cannot convert ‘const K*’ to ‘const X*’
// in initialization
return 0 ;
}
问题在于它依赖继承和构造函数调用才能有效。
添加一个带有函数的自制“约束”?
这个约束更像是一个静态断言,在调用方法时进行测试。一、约束函数:
template<typename T, typename T_Base>
void apply_inheritance_constraint()
{
// This code does nothing, and has no side effects. It will probably
// be optimized away at compile time.
const T_Base * t = static_cast<const T *>(NULL) ;
} ;
然后使用它的类:
template<typename T>
class D
{
public :
void foo()
{
// Here, we'll verify if T inherits from X
apply_inheritance_constraint<T, X>() ;
T t ;
t.kewl_method() ;
}
} ;
int main()
{
// D's constraint is : implements kewl_method, and derives from X
D<X> x ; // OK : x is of type X
D<Y> y ; // OK : y derives from X
D<Z> z ; // NOT OK : z won't compile: main.cpp error:
// cannot convert ‘const Z*’ to ‘const X*’
// in initialization
D<K> k ; // NOT OK : K won't compile: /main.cpp 2 errors:
// ‘class K’ has no member named ‘kewl_method’
// cannot convert ‘const K*’ to ‘const X*’
// in initialization
return 0 ;
}
但是,作为 A 和 B 的示例,您需要调用foo()
方法来阻止编译。
结论
您必须根据您的具体需求在上述方法中进行选择之一。
但通常情况下,就我而言,我发现这一切都太过分了,我会使用上面第一个更简单的解决方案。
编辑2011-07-24
添加了另一部分代码,以通过简单的函数调用来表达约束。
在“添加未使用的演员?”中部分,我替换了参考演员表X & x = t ;
使用指针转换(如其他部分所示),我认为这是更好的。
为了给凯撒应得的,指针转换最初的灵感来自于现在已删除答案 https://stackoverflow.com/questions/6803100/achieving-bounded-genericity-in-c/6803214#6803214 of 乔纳森·格林斯潘 https://stackoverflow.com/users/265530.