在Java中,你可以定义泛型类,它只接受你选择的扩展类的类型,例如:
public class ObservableList<T extends List> {
...
}
这是使用“extends”关键字完成的。
在c++中是否有与此关键字简单等价的东西?
在Java中,你可以定义泛型类,它只接受你选择的扩展类的类型,例如:
public class ObservableList<T extends List> {
...
}
这是使用“extends”关键字完成的。
在c++中是否有与此关键字简单等价的东西?
当前回答
最简单的解决办法是忽略这个问题,虽然还没有人提到过。如果我试图在一个期望容器类(如vector或list)的函数模板中使用int作为模板类型,那么我将得到一个编译错误。粗暴而简单,但它解决了问题。编译器将尝试使用您指定的类型,如果失败,则生成编译错误。
唯一的问题是,您得到的错误消息将很难阅读。然而,这是一种非常常见的方法。标准库中充满了函数或类模板,它们期望从模板类型中获得某些行为,而不做任何检查所使用的类型是否有效的工作。
如果您想要更好的错误消息(或者如果您想要捕捉不会产生编译器错误,但仍然没有意义的情况),您可以根据您想要的复杂程度,使用Boost的静态断言或Boost concept_check库。
使用最新的编译器,可以使用内置的static_assert。
其他回答
我们可以使用std::is_base_of和std::enable_if: (static_assert可以被删除,如果不能引用type_traits,上面的类可以自定义实现或从boost中使用)
#include <type_traits>
#include <list>
class Base {};
class Derived: public Base {};
#if 0 // wrapper
template <class T> class MyClass /* where T:Base */ {
private:
static_assert(std::is_base_of<Base, T>::value, "T is not derived from Base");
typename std::enable_if<std::is_base_of<Base, T>::value, T>::type inner;
};
#elif 0 // base class
template <class T> class MyClass: /* where T:Base */
protected std::enable_if<std::is_base_of<Base, T>::value, T>::type {
private:
static_assert(std::is_base_of<Base, T>::value, "T is not derived from Base");
};
#elif 1 // list-of
template <class T> class MyClass /* where T:list<Base> */ {
static_assert(std::is_base_of<Base, typename T::value_type>::value , "T::value_type is not derived from Base");
typedef typename std::enable_if<std::is_base_of<Base, typename T::value_type>::value, T>::type base;
typedef typename std::enable_if<std::is_base_of<Base, typename T::value_type>::value, T>::type::value_type value_type;
};
#endif
int main() {
#if 0 // wrapper or base-class
MyClass<Derived> derived;
MyClass<Base> base;
// error:
MyClass<int> wrong;
#elif 1 // list-of
MyClass<std::list<Derived>> derived;
MyClass<std::list<Base>> base;
// error:
MyClass<std::list<int>> wrong;
#endif
// all of the static_asserts if not commented out
// or "error: no type named ‘type’ in ‘struct std::enable_if<false, ...>’ pointing to:
// 1. inner
// 2. MyClass
// 3. base + value_type
}
c++ 20概念使用示例
改编自https://en.cppreference.com/w/cpp/language/constraints,你可以只做一些鸭子打字:
#include <cassert>
#include <concepts>
struct ClassWithMyFunc {
int myFunc() {
return 1;
}
};
struct ClassWithoutMyFunc {};
// Concept HasMyFunc: type 'T' has `.myFunc` and
// its return is convertible to int.
template<typename T>
concept HasMyFunc= requires(T a) {
{ a.myFunc() } -> std::convertible_to<int>;
};
// Constrained function template
template<HasMyFunc T>
int f(T t) {
return t.myFunc() + 1;
}
int main() {
assert(f(ClassWithMyFunc()) == 2);
// assert(f(ClassWithoutMyFunc()) == 2);
}
编译并运行:
g++ -ggdb3 -O0 -std=c++20 -Wall -Wextra -pedantic -o main.out main.cpp
./main.out
如果取消注释// assert(f(ClassWithoutMyFunc()) == 2);,它会像预期的那样失败:
In file included from /usr/include/c++/10/cassert:44,
from main.cpp:1:
main.cpp: In function ‘int main()’:
main.cpp:27:34: error: use of function ‘int f(T) [with T = ClassWithoutMyFunc]’ with unsatisfied constraints
27 | assert(f(ClassWithoutMyFunc()) == 2);
| ^
main.cpp:21:5: note: declared here
21 | int f(T t) {
| ^
main.cpp:21:5: note: constraints not satisfied
main.cpp: In instantiation of ‘int f(T) [with T = ClassWithoutMyFunc]’:
main.cpp:27:5: required from here
main.cpp:15:9: required for the satisfaction of ‘HasMyFunc<T>’ [with T = ClassWithoutMyFunc]
main.cpp:15:20: in requirements with ‘T a’ [with T = ClassWithoutMyFunc]
main.cpp:16:15: note: the required expression ‘a.myFunc()’ is invalid
16 | { a.myFunc() } -> std::convertible_to<int>;
| ~~~~~~~~^~
cc1plus: note: set ‘-fconcepts-diagnostics-depth=’ to at least 2 for more detail
需要多个基类
如果你真的想要某个基类:
#include <concepts>
#include <type_traits>
struct Base1 {};
struct Base2 {};
struct Derived1 : public Base1 {};
struct Derived2 : public Base2 {};
struct NotDerived {};
template<typename T>
concept HasBase1Or2= std::is_base_of<Base1, T>::value || std::is_base_of<Base2, T>::value;
template<HasBase1Or2 T>
void f(T) {}
int main() {
f(Derived1());
f(Derived2());
// f(NotDerived());
}
如果取消注释// f(NotDerived());它在以下情况下失败:
main.cpp: In function ‘int main()’:
main.cpp:22:19: error: use of function ‘void f(T) [with T = NotDerived]’ with unsatisfied constraints
22 | f(NotDerived());
| ^
main.cpp:17:6: note: declared here
17 | void f(T) {}
| ^
main.cpp:17:6: note: constraints not satisfied
main.cpp: In instantiation of ‘void f(T) [with T = NotDerived]’:
main.cpp:22:19: required from here
main.cpp:13:9: required for the satisfaction of ‘HasBase1Or2<T>’ [with T = NotDerived]
main.cpp:13:55: note: no operand of the disjunction is satisfied
13 | concept HasBase1Or2= std::is_base_of<Base1, T>::value ||
| ~~~~~~^~
14 | std::is_base_of<Base2, T>::value;
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cc1plus: note: set ‘-fconcepts-diagnostics-depth=’ to at least 2 for more detail
在Ubuntu 21.04 GCC 10.3.0上测试。
GCC 10似乎已经实现了它:https://gcc.gnu.org/gcc-10/changes.html,你可以在Ubuntu 20.04上获得它作为一个PPA。https://godbolt.org/ GCC 10.1无法在Ubuntu 20.04上识别概念。
没有用于此类类型检查的关键字,但你可以放入一些至少会以有序方式失败的代码:
(1)如果你想让一个函数模板只接受某个基类X的参数,将它赋给函数中的X引用。 (2)如果你想接受函数但不接受原语,反之亦然,或者你想用其他方式过滤类,在你的函数中调用一个(空的)模板帮助函数,它只为你想接受的类定义。
你也可以在一个类的成员函数中使用(1)和(2)对整个类强制进行这些类型检查。
你可以把它放到一些智能宏来减轻你的痛苦。:)
最简单的解决办法是忽略这个问题,虽然还没有人提到过。如果我试图在一个期望容器类(如vector或list)的函数模板中使用int作为模板类型,那么我将得到一个编译错误。粗暴而简单,但它解决了问题。编译器将尝试使用您指定的类型,如果失败,则生成编译错误。
唯一的问题是,您得到的错误消息将很难阅读。然而,这是一种非常常见的方法。标准库中充满了函数或类模板,它们期望从模板类型中获得某些行为,而不做任何检查所使用的类型是否有效的工作。
如果您想要更好的错误消息(或者如果您想要捕捉不会产生编译器错误,但仍然没有意义的情况),您可以根据您想要的复杂程度,使用Boost的静态断言或Boost concept_check库。
使用最新的编译器,可以使用内置的static_assert。
好吧,你可以这样创建你的模板:
template<typename T>
class ObservableList {
std::list<T> contained_data;
};
然而,这将使限制成为隐式的,而且您不能只提供任何看起来像列表的东西。还有其他方法来限制所使用的容器类型,例如使用特定的迭代器类型,这些迭代器类型并不存在于所有容器中,但同样,这是一种隐式限制而不是显式限制。
据我所知,目前的标准中不存在完全镜像Java语句的结构。
有一些方法可以通过在模板中使用特定的typedefs来限制在模板中使用的类型。这将确保模板专门化的编译不包括特定的类型定义将失败,所以你可以有选择地支持/不支持某些类型。
在c++ 11中,概念的引入会让这变得更容易,但我不认为它会完全满足你的需求。