在Java中,你可以定义泛型类,它只接受你选择的扩展类的类型,例如:
public class ObservableList<T extends List> {
...
}
这是使用“extends”关键字完成的。
在c++中是否有与此关键字简单等价的东西?
在Java中,你可以定义泛型类,它只接受你选择的扩展类的类型,例如:
public class ObservableList<T extends List> {
...
}
这是使用“extends”关键字完成的。
在c++中是否有与此关键字简单等价的东西?
当前回答
这在普通c++中是不可能的,但是你可以在编译时通过概念检查来验证模板参数,例如使用Boost的BCCL。
从c++ 20开始,概念开始成为该语言的官方特性。
其他回答
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上识别概念。
好吧,你可以这样创建你的模板:
template<typename T>
class ObservableList {
std::list<T> contained_data;
};
然而,这将使限制成为隐式的,而且您不能只提供任何看起来像列表的东西。还有其他方法来限制所使用的容器类型,例如使用特定的迭代器类型,这些迭代器类型并不存在于所有容器中,但同样,这是一种隐式限制而不是显式限制。
据我所知,目前的标准中不存在完全镜像Java语句的结构。
有一些方法可以通过在模板中使用特定的typedefs来限制在模板中使用的类型。这将确保模板专门化的编译不包括特定的类型定义将失败,所以你可以有选择地支持/不支持某些类型。
在c++ 11中,概念的引入会让这变得更容易,但我不认为它会完全满足你的需求。
这在普通c++中是不可能的,但是你可以在编译时通过概念检查来验证模板参数,例如使用Boost的BCCL。
从c++ 20开始,概念开始成为该语言的官方特性。
我建议使用Boost的静态断言特性与Boost Type Traits库中的is_base_of一致:
template<typename T>
class ObservableList {
BOOST_STATIC_ASSERT((is_base_of<List, T>::value)); //Yes, the double parentheses are needed, otherwise the comma will be seen as macro argument separator
...
};
在其他一些更简单的情况下,你可以简单地前向声明一个全局模板,但只为有效类型定义(显式或部分专门化):
template<typename T> class my_template; // Declare, but don't define
// int is a valid type
template<> class my_template<int> {
...
};
// All pointer types are valid
template<typename T> class my_template<T*> {
...
};
// All other types are invalid, and will cause linker error messages.
[Minor EDIT 6/12/2013:使用声明但未定义的模板将导致链接器,而不是编译器,错误消息]
在c++中是否有与此关键字简单等价的东西?
No.
根据你想要实现的目标,可能会有足够的(甚至更好的)替代品。
我已经看了一些STL代码(在linux上,我认为它是从SGI的实现派生出来的)。它有“概念断言”;例如,如果您需要一个能够理解*x和++x的类型,则概念断言将在一个什么都不做的函数(或类似的东西)中包含该代码。它确实需要一些开销,所以把它放在一个定义依赖于#ifdef debug的宏中可能是聪明的。
如果子类关系确实是你想知道的,你可以在构造函数中断言T instanceof list(除非它在c++中“拼写”不同)。这样,你就可以通过测试来避免编译器无法为你检查它。