c ++ 使用 concepts为约束引入名称

393 阅读3分钟

通过定义 concepts,可以为一个或多个约束引入名称。

模板(函数、类和变量模板)可以使用 concepts 来约束它们的能力(通过require子句或作为模板形参的直接类型约束)。然而,concepts 也是布尔编译时表达式(类型谓词),您可以在必须检查类型的任何地方使用(例如在if constexpr条件中)。

定义 concepts

concepts定义如下:

template<...>

concept name =..;

等号是必需的(你不能声明一个concepts而不定义它)。

在等号后面,可以指定任何转换为true或false的编译时表达式

concepts很像类型为boo1的constexpr变量模板,但是没有显式地指定类型

template<typename T>

 concept MyConcept=;

std::is_same<MyConcept<...>,bool> 


这意味着,在编译时或运行时,您总是可以在需要布尔表达式值的地方使用一个concepts。

但是,您不能接受该地址,因为它后面没有对象(它是一个prvalue)。

模板参数可能没有约束(不能使用一个concepts来定义一个concepts)。

你不能在函数内部定义concepts(所有模板都是这样)。

concepts 的特殊能力

concepts具有特殊的能力。如果你比较布尔变量模板的通常定义(因为它特别用于定义类型特征)

template<typename T>

inline constexpr bool IsOrHasThisOrThat =...; 

一个concepts的定义

template<typename T>

concept IsOrHasThisOrThat =...; 

我们有以下区别:

  • concepts并不代表代码。它们没有类型、存储空间、生命周期或与对象相关的任何其他属性。通过在编译时为特定的模板参数实例化它们,它们的实例化只会变成true或false。因此,你可以在任何你可以使用真或假的地方使用它们,你可以得到这些字面量的所有属性。

  • concepts不必声明为内联的,它们隐式地声明为内联。

  • concepts可以用作类型约束:

    template<IsOrHasThisOrThat T>
    

    变量模板不能这样使用。

  • concepts是给约束命名的唯一方法,这意味着您需要它们来决定是否使用约束

使用concepts作为类型约束

如前所述,concepts可以用作类型约束。

类型约束可以在不同的地方使用:

  • 在模板类型参数的声明中

  • 在用auto声明的调用参数的声明中

  • 在复合需求中的As需求中

例子:

template<std::integral T>

class MyClass {

...

};

auto myFunc(const std::integral auto& val) {

...

 };

template<typename T>

concept MyConcept = requires(T x) {

 	{x+x}→>std::integral;

};

在这里,我们使用为表达式返回的单个参数或类型调用的一元约束。

具有多个参数的类型约束

还可以使用带有多个参数的约束,然后将参数类型或返回值用作第一个参数

template<std::convertible_to<int> T>

class MyClass {

};

auto myFunc(const std::convertible_to<int> auto& val) { 

...

 };

template<typename T>

concept MyConcept = requires(Tx){

	{x+x}-> std::convertible_to<int>;

 };

另一个经常使用的例子是约束可调用对象(函数、函数对象、lambda)的类型,要求您可以使用std::invocable或std::regular invocable:例如,要要求传递一个接受int和std::string的操作,你必须声明

template<std::invocable<int, std::string> Callable>

void call(Callable op);

或者:

void call(std::invocable<int, std::string> auto op);

std::invocable和std::regular invocable之间的区别在于后者保证不修改传递的操作和参数。

这是语义上的差异,它只有助于记录意图。通常只使用std::invocable。

类型约束和auto

类型约束可以在所有可以使用 auto 的地方使用:

  • 要约束声明,请执行以下操作:

    std::integral auto val1 =42;
    
    std::integral auto val2 = true; 
    
     for (const std::integral auto& elem : coll) {
    ...
     }
    
  • 要约束返回类型,请执行以下操作:

    std::integral auto foo() { 
    ...
    }
    
    
  • 约束非类型模板参数:

    template<typename T, std::integral auto max>
     class SizedColl {
    ...
    };
    
    
  • 这也适用于带有多个参数的concepts

    template<typename T, std::convertible_to<T> auto default>
    
     class MyType {
    
    ...
    };
    

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 N 天,点击查看活动详情