概念很棒,不要误会我的意思,但是为什么我们需要另一个关键字呢?
考虑以下示例:
#include <type_traits>
template <typename T>
concept UnsignedConst = std::is_integral_v<T> && std::is_const_v<T>;
template <UnsignedConst T>
void foo(T bar);
我们也可以使用以下——在我看来更直接——语法:
/* ... */
template <typename T>
constexpr bool UnsignedConst = std::is_integral_v<T> && std::is_const_v<T>;
/* ... */
在幕后,概念只不过是取决于模板参数的编译时布尔常量。甚至
requires
子句也可以与变量模板一起使用,因为它们只是在编译时计算为布尔值的表达式。
在幕后,概念只不过是取决于模板参数的编译时布尔常量。
这是不真实的。
A
concept
具有单纯的 constexpr bool
所没有的特殊属性。特别是,concept
不能特化。这种无法专门化 使得概念包含规则 成为可能。它允许标准为模板的“更专业”版本制定规则。
鉴于以下情况:
template<typename T>
concept A = atomic_constraint_a<T>;
template<typename T>
concept B = atomic_constraint_a<T> && atomic_constraint_b<T>;
在 C++ 概念规则中很明显,
A
包含在 B
中。这意味着满足 T
的任何类型
B
必然 满足 A
。因此,如果您有两个模板,一个受 A
约束,一个受 B
约束,如果您传递满足 B
的类型,即使存在 A
的版本,也会选择该模板.
专门化会 ruin 这,因为有人可以来专门化
B
为类型 U
以这样的方式 A<U>
不包含 B<U>
.
那么……那会发生什么?当有人试图向具有竞争约束的上述模板提供
U
时会发生什么?主要约束说存在包含关系,但专门的约束没有。
更重要的是,我什至可以写两个具有包含关系的概念并确保这种关系得到维护吗?也就是说,在一个专业化的世界里,我可以在我的模板接口中依赖包容吗?
不,使包含成为可能的唯一方法是禁止专业化。但是变量模板可以专门化。所以现在你需要一个新的结构说“我有点像那个东西,但你不能专门化我”。我们可以让每个人都键入
template<typename T> [[nonspecialized]] inline constexpr bool concept_name = X
以获得包含(以及概念提供的其他东西)。
或者我们可以只说
template<typename T> concept concept_name = x
并消除一堆句法噪音。
此外,
concept
s 不能是类的成员。这也很重要,因为这意味着您不能通过专门化 around 概念的类来 de facto 专门化概念。
此外,通过使
concept
成为一种特殊的句法结构,由其自己的语法标记引入,它使语言能够更轻松地在新奇的地方使用 concept
。例如,std::integral auto var_name = func();
。编译器可以看出std::integral
是代表一个概念的标识符。如果它看到一个 constexpr bool
变量,您 可能 打算将它用作一个概念。或者您可能打算以其他方式使用它。通过使概念成为一种独特的事物,我们消除了任何歧义。
这就是允许我们在语法中以各种方式使用类型概念的原因,如果没有专门的语法,这将是……困难的事情。也许您可以将属性或其他内容应用于
constexpr inline bool
,但是再次...为什么所有关键字都只是指concept
?