在Java中,你可以定义泛型类,它只接受你选择的扩展类的类型,例如:
public class ObservableList<T extends List> {
...
}
这是使用“extends”关键字完成的。
在c++中是否有与此关键字简单等价的东西?
在Java中,你可以定义泛型类,它只接受你选择的扩展类的类型,例如:
public class ObservableList<T extends List> {
...
}
这是使用“extends”关键字完成的。
在c++中是否有与此关键字简单等价的东西?
当前回答
我认为之前所有的答案都是只见树木不见森林。
Java泛型与模板不同;它们使用类型擦除,这是一种动态技术,而不是编译时多态性,这是一种静态技术。这两种截然不同的策略不能很好地融合在一起的原因应该是显而易见的。
与其尝试使用编译时构造来模拟运行时构造,不如让我们看看extends实际上做了什么:根据Stack Overflow和Wikipedia, extends用于指示子类化。
c++也支持子类化。
还显示了一个容器类,它以泛型的形式使用类型擦除,并扩展以执行类型检查。在c++中,您必须自己执行类型擦除机制,这很简单:创建一个指向超类的指针。
让我们把它包装成一个类型定义,让它更容易使用,而不是一个完整的类,等等:
Typedef std::list<superclass*> subclasses_of_superclass_only_list;
例如:
class Shape { };
class Triangle : public Shape { };
typedef std::list<Shape*> only_shapes_list;
only_shapes_list shapes;
shapes.push_back(new Triangle()); // Works, triangle is kind of shape
shapes.push_back(new int(30)); // Error, int's are not shapes
现在,List似乎是一个接口,表示一种集合。c++中的接口只是一个抽象类,也就是说,一个只实现纯虚方法的类。使用这种方法,您可以轻松地在c++中实现java示例,而不需要任何概念或模板专门化。由于虚拟表查找,它的执行速度也会像Java样式的泛型一样慢,但这通常是可以接受的损失。
其他回答
没有用于此类类型检查的关键字,但你可以放入一些至少会以有序方式失败的代码:
(1)如果你想让一个函数模板只接受某个基类X的参数,将它赋给函数中的X引用。 (2)如果你想接受函数但不接受原语,反之亦然,或者你想用其他方式过滤类,在你的函数中调用一个(空的)模板帮助函数,它只为你想接受的类定义。
你也可以在一个类的成员函数中使用(1)和(2)对整个类强制进行这些类型检查。
你可以把它放到一些智能宏来减轻你的痛苦。:)
class Base
{
struct FooSecurity{};
};
template<class Type>
class Foo
{
typename Type::FooSecurity If_You_Are_Reading_This_You_Tried_To_Create_An_Instance_Of_Foo_For_An_Invalid_Type;
};
确保派生类继承了FooSecurity结构,编译器就会在所有正确的地方出错。
只接受类型List派生的类型T的等效函数如下所示
template<typename T,
typename std::enable_if<std::is_base_of<List, T>::value>::type* = nullptr>
class ObservableList
{
// ...
};
我认为之前所有的答案都是只见树木不见森林。
Java泛型与模板不同;它们使用类型擦除,这是一种动态技术,而不是编译时多态性,这是一种静态技术。这两种截然不同的策略不能很好地融合在一起的原因应该是显而易见的。
与其尝试使用编译时构造来模拟运行时构造,不如让我们看看extends实际上做了什么:根据Stack Overflow和Wikipedia, extends用于指示子类化。
c++也支持子类化。
还显示了一个容器类,它以泛型的形式使用类型擦除,并扩展以执行类型检查。在c++中,您必须自己执行类型擦除机制,这很简单:创建一个指向超类的指针。
让我们把它包装成一个类型定义,让它更容易使用,而不是一个完整的类,等等:
Typedef std::list<superclass*> subclasses_of_superclass_only_list;
例如:
class Shape { };
class Triangle : public Shape { };
typedef std::list<Shape*> only_shapes_list;
only_shapes_list shapes;
shapes.push_back(new Triangle()); // Works, triangle is kind of shape
shapes.push_back(new int(30)); // Error, int's are not shapes
现在,List似乎是一个接口,表示一种集合。c++中的接口只是一个抽象类,也就是说,一个只实现纯虚方法的类。使用这种方法,您可以轻松地在c++中实现java示例,而不需要任何概念或模板专门化。由于虚拟表查找,它的执行速度也会像Java样式的泛型一样慢,但这通常是可以接受的损失。
最简单的解决办法是忽略这个问题,虽然还没有人提到过。如果我试图在一个期望容器类(如vector或list)的函数模板中使用int作为模板类型,那么我将得到一个编译错误。粗暴而简单,但它解决了问题。编译器将尝试使用您指定的类型,如果失败,则生成编译错误。
唯一的问题是,您得到的错误消息将很难阅读。然而,这是一种非常常见的方法。标准库中充满了函数或类模板,它们期望从模板类型中获得某些行为,而不做任何检查所使用的类型是否有效的工作。
如果您想要更好的错误消息(或者如果您想要捕捉不会产生编译器错误,但仍然没有意义的情况),您可以根据您想要的复杂程度,使用Boost的静态断言或Boost concept_check库。
使用最新的编译器,可以使用内置的static_assert。