在Java中,你可以定义泛型类,它只接受你选择的扩展类的类型,例如:
public class ObservableList<T extends List> {
...
}
这是使用“extends”关键字完成的。
在c++中是否有与此关键字简单等价的东西?
在Java中,你可以定义泛型类,它只接受你选择的扩展类的类型,例如:
public class ObservableList<T extends List> {
...
}
这是使用“extends”关键字完成的。
在c++中是否有与此关键字简单等价的东西?
当前回答
没有用于此类类型检查的关键字,但你可以放入一些至少会以有序方式失败的代码:
(1)如果你想让一个函数模板只接受某个基类X的参数,将它赋给函数中的X引用。 (2)如果你想接受函数但不接受原语,反之亦然,或者你想用其他方式过滤类,在你的函数中调用一个(空的)模板帮助函数,它只为你想接受的类定义。
你也可以在一个类的成员函数中使用(1)和(2)对整个类强制进行这些类型检查。
你可以把它放到一些智能宏来减轻你的痛苦。:)
其他回答
据我所知,这在c++中是不可能的。不过,我们计划在新的c++ 0x标准中添加一个称为“概念”的特性,它可以提供您正在寻找的功能。这篇关于c++概念的维基百科文章将更详细地解释它。
我知道这并不能立即解决您的问题,但有一些c++编译器已经开始从新标准中添加特性,因此可能会找到一个已经实现了概念特性的编译器。
最简单的解决办法是忽略这个问题,虽然还没有人提到过。如果我试图在一个期望容器类(如vector或list)的函数模板中使用int作为模板类型,那么我将得到一个编译错误。粗暴而简单,但它解决了问题。编译器将尝试使用您指定的类型,如果失败,则生成编译错误。
唯一的问题是,您得到的错误消息将很难阅读。然而,这是一种非常常见的方法。标准库中充满了函数或类模板,它们期望从模板类型中获得某些行为,而不做任何检查所使用的类型是否有效的工作。
如果您想要更好的错误消息(或者如果您想要捕捉不会产生编译器错误,但仍然没有意义的情况),您可以根据您想要的复杂程度,使用Boost的静态断言或Boost concept_check库。
使用最新的编译器,可以使用内置的static_assert。
在c++中是否有与此关键字简单等价的东西?
No.
根据你想要实现的目标,可能会有足够的(甚至更好的)替代品。
我已经看了一些STL代码(在linux上,我认为它是从SGI的实现派生出来的)。它有“概念断言”;例如,如果您需要一个能够理解*x和++x的类型,则概念断言将在一个什么都不做的函数(或类似的东西)中包含该代码。它确实需要一些开销,所以把它放在一个定义依赖于#ifdef debug的宏中可能是聪明的。
如果子类关系确实是你想知道的,你可以在构造函数中断言T instanceof list(除非它在c++中“拼写”不同)。这样,你就可以通过测试来避免编译器无法为你检查它。
这在c++中通常是不合理的,正如这里的其他答案所指出的那样。在c++中,我们倾向于基于其他约束来定义泛型类型,而不是“从该类继承”。如果你真的想这样做,在c++ 11和<type_traits>中很容易做到:
#include <type_traits>
template<typename T>
class observable_list {
static_assert(std::is_base_of<list, T>::value, "T must inherit from list");
// code here..
};
This breaks a lot of the concepts that people expect in C++ though. It's better to use tricks like defining your own traits. For example, maybe observable_list wants to accept any type of container that has the typedefs const_iterator and a begin and end member function that returns const_iterator. If you restrict this to classes that inherit from list then a user who has their own type that doesn't inherit from list but provides these member functions and typedefs would be unable to use your observable_list.
There are two solutions to this issue, one of them is to not constrain anything and rely on duck typing. A big con to this solution is that it involves a massive amount of errors that can be hard for users to grok. Another solution is to define traits to constrain the type provided to meet the interface requirements. The big con for this solution is that involves extra writing which can be seen as annoying. However, the positive side is that you will be able to write your own error messages a la static_assert.
为了完整起见,上面例子的解决方案如下:
#include <type_traits>
template<typename...>
struct void_ {
using type = void;
};
template<typename... Args>
using Void = typename void_<Args...>::type;
template<typename T, typename = void>
struct has_const_iterator : std::false_type {};
template<typename T>
struct has_const_iterator<T, Void<typename T::const_iterator>> : std::true_type {};
struct has_begin_end_impl {
template<typename T, typename Begin = decltype(std::declval<const T&>().begin()),
typename End = decltype(std::declval<const T&>().end())>
static std::true_type test(int);
template<typename...>
static std::false_type test(...);
};
template<typename T>
struct has_begin_end : decltype(has_begin_end_impl::test<T>(0)) {};
template<typename T>
class observable_list {
static_assert(has_const_iterator<T>::value, "Must have a const_iterator typedef");
static_assert(has_begin_end<T>::value, "Must have begin and end member functions");
// code here...
};
上面的例子中有很多概念展示了c++ 11的特性。对于好奇的人来说,一些搜索词是可变参数模板、SFINAE、表达式SFINAE和类型特征。
只接受类型List派生的类型T的等效函数如下所示
template<typename T,
typename std::enable_if<std::is_base_of<List, T>::value>::type* = nullptr>
class ObservableList
{
// ...
};