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,