我正在将项目从Java转移到C ++,而我在Java中遇到一些相对简单的问题。
我有一个类X
,用于处理Y
类型的对象和从Y
继承的对象。 X
经常需要调用来自Y
的方法,比如kewl_method()
,并且此方法在从Y
继承的每个类中都有所不同。在Java中,我可以这样做:
public class X<y extends Y>
我会在kewl_method()
中致电X
而没有任何头痛,它会做我想要的。如果我理解正确(我是C ++的新手),C ++中没有有限的通用性,所以如果我使用X
的模板,就可以用绝对的东西来填充它,我就赢了能够调用kewl_method()
。
在C ++中执行此操作的最佳方法是什么?使用演员?
限制:我不能使用boost或TR1。
答案 0 :(得分:13)
TravisG(是:heishe)already answered,就我而言。
但我想对你的问题发表评论:
所以,如果我使用带X的模板,就可以用绝对的任何东西填充它
不,因为如果没有可访问的kewl_method
,它就无法编译。
你必须记住,在Java中,有限的通用性不是像你想象的那样限制你的泛型类接受的类型,而是更多关于为泛型类提供关于它的泛型类型T的更完整的信息,以便能够在编译时验证对其方法的调用。
在C ++中,此功能按原样提供并由编译器使用:在类似于duck typing的方式中,但在编译时解析,编译器将仅在泛型类型时接受方法的编译class可以访问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 ++模板,您可以提供模板化的类A:
template<typename T>
class A
{
public :
void foo()
{
T t ;
t.kewl_method() ;
}
} ;
...使用X,Y和Z类,但不是K,因为:
kewl_method()
kewl_method()
kewl_method()
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++ concepts标准化......)它们错过了C ++ 0x截止日期,所以我想我们将不得不等待下一个标准...... )
如果确实想要放置约束,有多种方法。 虽然我知道它,但我不熟悉SFINAE概念给你解决方案,但是我仍然可以看到两种方法来应用约束对于你的情况(当他们测试g ++ 4.4.5时,有人可以更聪明地验证我的代码吗?):
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()
方法来阻止编译。
根据您的具体需要,您必须在上述方法之一中进行选择。
但通常情况下,就我而言,我发现所有这些都太过分了,我会使用上面第一个更简单的解决方案。
添加了另一个带有代码的部分,通过简单的函数调用来表达约束。
在“添加未使用的演员?”中部分,我用指针转换替换了引用转换X & x = t ;
(如在其他部分中),我认为这更好。
为了给凯撒带来应有的价值,指针演员最初的灵感来自now deleted answer Jonathan Grynspan中的一行代码。
答案 1 :(得分:4)
只需输入:
template<class T>
void method()
{
T t;
t.kewl_method();
}
如果你以错误的方式使用它,你的编译器会给你一个错误,说kewl_method()不是[type of T]的成员。
答案 2 :(得分:2)
如果我理解你正确地使Y::kewl_method
虚拟是一个很好的Java翻译。你要在从Y
派生的类中重载它。
答案 3 :(得分:0)
您在Java中引用的构造是泛型。在C ++中找到的最接近的对应物是模板。 因此,您在C ++中提供的最接近的翻译将是
template<class T>
class X
{
// Here you use T as Y or type derived from it
// for example
void method(T& y)
{
y.kewl_method();
}
};
用法如下:
X<Y> a;
或者,如果您来自Y
类型,请说Z
:
class Z : public Y {
// ...
};
您也可以使用
X<Z> b;
这是可以接受的。模板是用C ++编写的,即你可以调用T
类型的任何方法,只要实际指定的类型具有这种方法,它就是正确的。
答案 4 :(得分:0)
据我了解,您希望强制传递给您的类模板的模板参数继承自某个class Y
。我应该注意到,你不能使用助推器是非常不幸的。我将提供解决方案 提升。
template <class T, bool OK> class XImpl; //no definition, just declaration
template <class T> class XImpl<T, true> //partial specialization
{
//here goes your real class definition
};
template <class T> class X : public XImpl<T, boost::is_base_and_derived<Y, T>::value >
{}; //if T isn't derived from Y, compilation will fail because you are trying to
//inherit from an incomplete (undefined) type
我们使用的唯一boost
部分是is_based_and_derived
元函数。您可以查看boost源,了解它是如何实现的,并自行实现。 HTH
答案 5 :(得分:0)
使用模板,但对其使用的类型加以限制:
template<typename T>
void
X::some_member(T& t)
{
// Triggers a compile-time error if/when T is not derived from Y
STATIC_ASSERT(( is_base_of<Y, T> ));
t.kewl_method();
}
由于您未使用Boost,因此您必须实施STATIC_ASSERT
和is_base_of
。第一个有点容易,但第二个不是这样(尽管你可以使用convertible<T, Y>
特征作为近似值。)