c++概念约束(concept, requires)

concept

定义概念

template<typename T>
concept integral=is_integral_v<T>;

template<typename T>
concept floating_point=is_floating_point_v<T>;

int main()
{
    static_assert(  floating_point<float>  );
    static_assert( ! floating_point<int>  );
}

先检查约束的表达式是否合法, 合法时, 当约束表达式符合时返回true

不合法或不符合返回false

当约束表达式不为bool将引发编译错误

template<typename T>
concept t=1; //错误, 1为int类型不为bool类型

约束组合, 通过&&(约束合取)和 ||(约束析取)

template<typename T>
concept C=is_integral_v<typename T::type>||(sizeof(T)>1);

static_assert(  C<double>  );

定义约束C为 表达式是否合法(存在类型成员type)且为整型类型 || 大小>1

static_assert(  C<double>  );中,  double不存在成员type, 但大小>1, 是符合约束的

约束组合与原子约束

对于可变模板形成的约束表达式, 既不是约束合取也不是约束析取, 而是原子约束

template<typename ...Ts>
concept C=(is_integral_v<typename Ts::type>||...);

先检查整个表达式是否合法. 即所有的参数都需要有类型成员type, 才能符合约束

而上面约束析取里第一个式子没有type成员但满足第二个式子, 也能符合约束 

约束组合: 每个式子的不合法不影响其他式子的判断

原子约束: 只要有一个式子不合法, 整个约束不成立

若要表达至少一个参数存在类型type且为整型, 可以添加一层间接层解决

template<typename T>
concept IntegralWithNestType=is_integral_v<typename T::type>;

template<typename ...Ts>
concept C(IntegralWithNestType<Ts>||...);

约束组合和原子约束的写法区别

//约束组合
template<typename T,typename U>
concept C1=is_integral_v<typename T::type>||is_integral_v<typename U::type>;

//原子约束        
template<typename T,typename U>
concept C2=bool(is_integral_v<typename T::type>||is_integral_v<typename U::type>)

原子约束需要在外层添加 bool()

测试

struct A
{
    using type = int;
};
static_assert(  C1<A,int>  );
static_assert(  ! C2<A,int>  );

C1中是约束组合, A满足约束(存在int的类型成员type)即成立

C2是原子约束, int不满足约束不成立

约束的否定形式

特殊的是关于否定

template<typename T>
concept C1=is_integral_v<typename T::type>;

template<typename T>
concept C2= ! is_integral_v<typename T::type>;

struct A
{
    using type = float;
};

static_assert(!C1<int>);
static_assert(!C2<int>);
static_assert(C2<A>);

概念约束先检查表达式是否合法, 再检查是否符合约束

C2的否定似乎有点违反直觉, 因为代码看上去C2是C1的否定

但C1和C2都需先检查表达式的合法性, C2只是否定为整型

表述就是

C1= 合法&&is_integral

C2=合法&& ! is_integral

如果要真正表达C1的否定, 如下面的表述, 即不存在类型T::type 或存在但不为整型,

C1= 合法&&is_integral

C2=! (合法 && is_integral)== 不合法 || ! is_integral

则需如下形式

template<typename T>
concept C3=!C1<T>;

static_assert(  C3<A>  );
static_assert(  C3<int>  );

requires与concept

简单要求 

template<typename M>
concept Machine=requires(M m)
{
    m.powerUp();        //需要存在成员函数powerUp, powerDown
    m.powerDown();
};

template<typename T>
concept Animal=requires(T animal)
{
    play(animal);   //要求存在函数play(T)
    T::count;       //要求存在静态成语count
    animal.age;     //要求存在成员变量age
};

template<typename T>
concept Number=requires(T a,T b,T c)
{
    a==a;       //要求能判等
    a+b*c;      //要求能进行+和*运算
};

template<typename T>
concept C=requires
{
    typename T::type;   //要求存在类型成员type
    typename vector<T>; //要求能够与vector组合进行模板实例化
};

T::count是要求有成员变量count

typename T::type是要求有类型成员type

复合要求: {表达式}可选的noexcept, 可选的返回类型概念要求

要求重载的move移动不能抛出异常: 

template<typename T>
concept Movable=requires(T a,T b)
{
    {a=move(b)}noexcept;
};

对返回类型要求

int f(int);
double g(int);

template<typename T>
concept C=requires(T x)
{
    {f(x)} -> same_as<T>;       //要求f(T)返回类型和T一致
    {g(x)} -> convertible_to<T>;//要求g(T)返回类型能和T相互转换
};


//f的返回类型为int, 和int一致
//g的返回类型为double, 能和int转换
static_assert(  C<int>  );

嵌套要求

外层requires只检查合法性, 而里层的require除了检查合法性外还要求返回bool类型的值

template<typename T>
concept C=requires
{
    typename T::type;           //外层requires只检查合法性
    //requires typename T::type;    错误,里层requires需要返回一个bool类型的值
    requires sizeof(T)> sizeof(void*);  //要求大小大于指针大小
    requires is_trivial_v<T>;           //要求类型是平凡的
};

嵌套要求的requires注意

以下的requires只检查合法性, 只要存在sizeof(T)即成立, 违反本意

requires{
    sizeof(T) <= sizeof(int);
};

以下的requires才会进行求值判断 

requires{
    requires sizeof(T) <= sizeof(int);
};

以下会报错, 理论上里面的requires实际并不是嵌套要求

requires(T t)
{
    requires(typename T::value_type x)
    {
        ++x;
    };
};

以下表达嵌套要求

requires(T t)
{
    requires requires(typename T::value_type x)
    {
        ++x;
    };
};

requires与编译器bool常量

requires不一定需要定义concept时出现, 只要接收编译器bool表达式的地方都运行出现

如果存在swap成员函数则has_member_swap=true

template<typename T>
constexpr bool has_member_swap=requires(T a,T b)
{
    a.swap(b);
};

requires还可以不和模板一起使用

constexpr bool has_member_swap1=requires(int a,int b)
{
    a.swap(b); //此时编译错误!, 而不是has_member_swap1=false
};
struct A
{
    void swap(A a);
};
constexpr bool has_member_swap2=requires(A a,A b)
{
    a.swap(b); //此时编译通过, has_member_swap2=true
};

对比上面两个, 如果不用模板, 要么编译错误, 要么编译通过且值为true

requires与非类型模板参数

template<size_t N>
concept Even=requires
{
    requires(N%2==0);
};

requires与if constexpr

template<typename T>
void clever_swap(T &a,T &b)
{
    if constexpr (requires(T a,T b){a.swap(b);})
    {
        a.swap(b);
    }
    else
    {
        std::swap(a,b);
    }
}

若存在则调用成员函数swap, 否则调用std::swap


 

require只检查表达式体,  而当形参无效时(没有value_type类型成员)

template<typename T>
constexpr bool P=requires(typename T::value_type v){++v;};

constexpr bool a=P<int>;

gcc接受, 编译通过返回假(a=false)

clang不接受, 编译错误

requires子句

为模板类或者模板函数添加约束

template<typename T>
requires is_integral_v<T>
T gcd(T a,T b){}

gcd(1.0,2.0);  //错误, double不满足is_integral_v
gcd(1,2);

requires约束与重载函数

template<typename T>    //受约束版本
requires is_integral_v<T>
void f(T){cout<<(  "1"  )<<endl;}


template<typename T>    //通用版本
void f(T){cout<<(  "2"  )<<endl;}

f(1);   //1
f(vector<int>{});//2

若用曾经的enable_if_t元编程技巧为

template<typename T>
enable_if_t<is_trivial_v<T>> f(T){cout<<(  "1"  )<<endl;}

template<typename T>
enable_if_t<!is_trivial_v<T>> f(T){cout<<(  "2"  )<<endl;}

requires约束与模板类

template<typename T>
class Optional
{
    union{T val;char dummy;}storage;
    bool initialized{};
};

template<typename T>
requires is_trivial_v<T>
class Optional<T>
{
    union{T val;char dummy;}storage;
    bool initialized{};
};

模板参数符合is_trivial_v时将使用特化版本

concept约束放在模板参数前

template<integral T,integral U>
void f(T,U);

相当于

template<typename T,typename U>
requires(integral<T> && integral<U>)
void f(T,U);

concept放在函数参数前

void f(integral auto a,integral auto b);

综合运用扩展transform

扩展transform算法使其能接收多个输入区间, 允许区间长度不等, 以最短的为准, 同时传入多元操作, 输出迭代器, 将变化结果写入输出迭代器

template<input_iterator...InputIt,      //要求一个或多个输入迭代器
         invocable<iter_reference_t<InputIt>...>Operation,  //多元操作符(可调用对象), 其中参数为对应迭代器解引用的类型
         output_iterator<invoke_result_t<Operation, iter_reference_t<InputIt>...>> OutputIt>   //输出迭代器类型, 要求接受多元操作符的返回结果
OutputIt zip_transform(OutputIt out, Operation op, pair<InputIt, InputIt>...inputs) //可变参数inputs需放在最后
{
    while (((inputs.first!=inputs.second)&&...))
    {
        *out++=op(*inputs.first++...);
    }
    return out;
}

int main()
{
    vector v1{1, 2, 3, 4};
    vector v2{5, 6, 7, 8};
    vector v3{9, 10, 11, 12};
    vector<int> result(4);
    zip_transform(result.begin(),[](int a,int b,int c){return a+b+c;}, 
                  make_pair(v1.begin(),v1.end()), 
                  make_pair(v2.begin(),v2.end()),
                  make_pair(v3.begin(),v3.end()));

    for (const auto &item: result)
    {
        cout<<(  item  )<<",";
    }
}

15,18,21,24,

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值