跳转至

第4章 详细介绍概念、需求和约束

目录

4.1 约束

这里的约束其实就是对泛型参数的要求, 可以约束函数模板, 类模板, 变量模板和别名模板. 类似以下的例子

template <typename T>
concept MyConcept = requires(T t) {
    { t + 1 } -> std::convertible_to<int>;
    { t - 1 } -> std::convertible_to<int>;
    { t * 2 } -> std::convertible_to<int>;
    { t / 2 } -> std::convertible_to<int>;
};

template <typename T>
    requires MyConcept<T>
void foo(T value) {
    fmt::print("Value: {}\n", value);
}

4.2 需求项

requires子句使用关键字requires和编译时布尔表达式来限制模板的可用性. 其中, 布尔表达式可以是: + 编译时的布尔表达式(编译时变量或编译时函数, 由constexprconstinit定义) + 概念 + requires表达式

template <typename T>
    requires(sizeof(T) > 4) && requires { typename T::value_type; }
void bar(T value) {}

void test_bar() {
    std::vector<int> vec = {1, 2, 3};
    bar(vec); // Ok, std::vector<int> has value_type
}

4.3 特别的布尔表达式

其实这里强调的是requires表达式的编译期特性, 如编译时变量/编译时函数.

namespace special_requires_expr {
    template <typename T, std::size_t N>
    requires(N > 0 && (N % 16) == 0 && std::same_as<T, int>)
    void foo(T (&arr)[N]) {
        fmt::print("Array size: {}\n", N);
    }
    void test_foo() {
        int arr[16] = {0};
        foo(arr); // Ok, N is 16, T is int
    }
}

一些常使用的概念: + 判断类型T是否是指针: std::is_pointer_v<T> + 判断类型T是否是nullptr: std::same_as<T, nullptr_t> + 判断类型T能否转为字符串: std::convertible_to<T, std::string>, 也可以使用std::is_convertible_v<T> + 虚空创建一个类型为T的对象: std::declare<T>()

4.4 需求表达式

语法糖, 其实就是允许requires之后跟着大量的expression, 注意这里的requires表达式的写法

template <typename T>
concept myconcept =  requires {
    typename T::value_type::first_type;
    typename T::value_type::second_type;
};

template <typename T>
    requires requires {
        typename T::value_type::first_type;
        typename T::value_type::second_type;
    }
void func(const T &) {
    // ...
}

void test_func() {
    std::map<int, std::string> map;
    func(map); // Ok, std::map<int, std::string> has value_type with first_type
               // and second_type
}

4.5 概念详解

概念的定义:

template <typename T>
concept name = ...;

  • 概念不表示代码, 没有类型、存储、生命周期活与对象相关的其他属性
  • 概念不必声明为内联的,可以隐士内联
  • 概念可以作为类型约束
  • 概念是给约束命名的唯一方法

非类型模板参数(NTTP)也可以作为概念(concept)

template<auto val>
concept LessThan10 = val < 10;

// 要求是2的幂
template<auto val>
concept PowerOf2 = std::has_single_bit(static_cast<unsigned>(val));

template<typename T, auto val>
requires PowerOf2<val> 
struct memory {};

// 不能这么写, 这种写法的意思是限制它的类型, 而不是值
template<typename T, PowerOf2 auto val>
struct memory {};