如果知道我会死在哪里,那我将永远不去那个地方 -查理 芒格
定义
概念(concepts),作为模板元编程(类模板和函数模板)的一部分,提出对模板实参的要求,进而影响模板的特化和函数重载。在编译期对实参求值,检测实参是否满足概念所提出的要求。
由以上定义中“在编译期对实参求值,检测实参是否满足概念所提出的要求”可知concepts实则为可以在编译期进行求值的模板类型的bool型变量。
进而的,concepts作为编译期进行求值的bool型变量,则可以对其进行&&、||、!操作,即concepts可以是单个/多个条件的交集、并集、补集。
定义
代码语言:javascript复制template < 模板形参列表 >
concept 概念名 属性(可选) = 约束表达式;
使用示例
代码语言:javascript复制#include <concepts>
template <typename T>
concept my_integeral = std::integral<T>;
template<my_integeral T>
T add(T a, T b)
{
return a b;
}
void using_concept()
{
add(3,5);
add(3.0, 5.0);//error
}
性质
概念不能递归地提及自身
代码语言:javascript复制template<typename T>
concept V = V<T*>; //V<T>要求V<T*>也为真。这种递归定义在C 的概念中是不被允许的,会导致编译错误。
template<V C>
C sub(C a, C b)
{
return a-b;
}
概念不能有关联的约束
代码语言:javascript复制template<C1 T>
concept Error1 = true; //不能接受C1约束
template<class T> requires std::is_integral_v<T>
concept Error2 = true; //不能接受requires约束
概念不能被显式实例化、显式特化或
部分特化
- 显式实例化:尝试为模板创建一个特定的类型实例。例如,对于一个模板类TemplateClass,我们可以尝试创建TemplateClass<int>的一个实例。
- 显式特化:为模板提供一个特定的类型版本。例如,我们可以为TemplateClass<int>提供一个特定的实现。
- 部分特化:为模板的某些类型参数提供特定的实现。例如,我们可以为TemplateClass<T, int>提供一个特定的实现。
template <typename T>
concept bool MyConcept = /* ... */;
// 以下都是错误的
MyConcept<int> obj; // 错误:概念不能被显式实例化
template <>
concept bool MyConcept<int> = /* ... */; // 错误:概念不能被显式特化
template <typename U>
concept bool MyConcept<U, int> = /* ... */; // 错误:概念不能被部分特化
概念可以在标识表达式中使用
该标识表达式的值在满足约束表达式时是 true,否则是 false
代码语言:javascript复制template <typename T>
concept bool MyConcept = requires(T t) {
{ t.foo() } -> std::same_as<void>;
};
int main() {
if constexpr (MyConcept<MyClass>) {
// MyClass满足MyConcept
} else {
// MyClass不满足MyConcept
}
}
使用方法
常规使用方法
代码语言:javascript复制template <typename T>
concept SignedIntegral = std::is_integral_v<T> && std::is_signed_v<T>;
template <SignedIntegral T>
void f1(T v)
{
}
template <typename T>
requires SignedIntegral<T>
void f2(T v)
{
}
template <typename T>
void f3(T v) requires SignedIntegral<T>
{
}
void f4(SignedIntegral auto v)
{
}
template <SignedIntegral auto v>
void g2()
{
}
lambda表达式中使用
代码语言:javascript复制auto f = []<SignedIntegral T> (T v) {
// ...
};
auto f = []<typename T> requires SignedIntegral<T>(T v) {
// ...
};
auto f = []<typename T> (T v) requires SignedIntegral<T> {
// ...
};
auto f = [](SignedIntegral auto v) {
// ...
};
auto g = []<SignedIntegral auto v> () {
// ...
};
参考文献:
https://zhuanlan.zhihu.com/p/266086040
Constraints and concepts (since C 20) - cppreference.com