concepts设计指南
处理多个需求
例如,迭代器需求的concepts检查可能如下所示
template<typename I>
concept IsIterator =
default_initializable<T> &&
std::copyable<I>&&
requires(I i) {
typename iter_difference_t<I>;
*i;
{++i} -> same_as<I&>;
{ i++ } →> same_as<I>; };
concepts与特征和表达
concepts不仅仅是在编译时计算布尔结果的表达式。与类型特征和其他编译时表达式相比,我通常更喜欢使用它们。
标准concepts更好地支持重载解决方案
例如,编译器能够检测一个concepts是否是另一个concepts的一部分。
考虑下面的例子,我们用两个定义为类型特征的需求重载函数foo()
template<typename T, typename U>
requires std::is_same_v<T,U>
void foo(T,U)
{
std::cout<<"foo() for parameters of same type" <<'\n';
}
template<typename T, typename U>
requires std::is_same_v<T, U> && std::is_integral_v<T>
void foo(T,U)
{
std::cout<<"foo() for integral parameters of same type"<<'\n'; }
foo(1,2);
问题是,如果两个要求都计算为 true,则两个重载都适合,并且没有规则表明其中一个优先于另一个。
因此,编译器停止编译将出现歧义错误。
如果我们改用相应的concepts,编译器会发现第二个要求是专业化,如果同时满足这两个要求,则首选它:
template<typename T, typename U>
requires std::same_as<T, U>
//using concepts void foo(T,U)
{
std::cout<<"foo() for parameters of same type" <<'\n';
}
template<typename T,typename U>
requires std::same_as<T, U> && std::integral<T>
void foo(T,U)
{
std::cout <<"foo() for integral parameters of same type"<<'\n';
}
foo(1,2);
谨慎定义concepts
为了检测子concepts,编译器必须能够检测到这些concepts以及它们被称为匹配的方式。这需要对concepts进行仔细的定义。例如,假设我们定义了自己的conceptsSameAs,并按如下方式使用它:
template<typename T, typename U>
concept SameAs = std::is_same_v<T,U>;
template<typename T, typename U>
requires SameAs<T,U>
void foo(T,U)
{
std::cout<<"foo() for parameters of same type" <<'\n'; }
template<typename T, typename U>
requires SameAs<T,U> && std::integral<T>
void foo(T,U)
{
std::cout<<"foo() for integral parameters of same type" <<'\n';
}
foo(1,2);
但是,请注意,如果在第二个foo()的要求中调用SameAs<>时交换参数会发生什么情况。
template<typename T, typename U>
concept SameAs =std::is_same_v<T,U>;
template<typename T,typename U>
requires SameAs<T, U>
void foo(T,U)
{
std::cout<<"foo() for parameters of same type" <<'\n'; }
template<typename T, typename U>
requires SameAs<U, T> && std::integral<T>
void foo(T,U)
{
std::cout<<"foo() for integral parameters of same type" <<'\n'; }
foo(1,2);
问题是编译器无法检测到SameAs<>是可交换的。顺序可能很重要,因此第一个需求不一定是第二个需求的子集。
但是,我们可以通过明确SameAs<>元素的顺序不重要来解决这个问题。这需要一个助手concepts
template<typename T, typename U>
concept SameAsHelper = std::is_same_v<T, U>; template<typename T, typename U>
concept SameAs = SameAsHelper<T, U> && SameAsHelper<U, T
现在,参数的顺序对于IsSame不再重要
template<typename T,typename U>
requires SameAs<T, U>
void foo(T,U)
{
std::cout << "foo() for parameters of same type" <<'\n'; }
template<typename T, typename U>
requires SameAs<U,T> && std::integral<T>
void foo(T,U)
{
std::cout<<"foo() for integral parameters of same type" <<'\n'; }
foo(1,2);
编译器可以发现第一个构建块SameAs<U,T>是SameAs定义的子concepts的一部分,因此其他构建块SameAs<T,U>和std::integral是一个扩展。因此,第二个foo()现在具有优先级。
c++标准知道这样的问题,因此定义了std::same as accord这个concepts,这样元素的顺序就无关紧要了
template<typename T, typename U> requires std::same_as<T,U>
void foo(T,U)
{
std::cout<<"foo() for parameters of same type" <<'\n'; }
template<typename T,typename U>
requires std::same_as<U, T> && std::integral<T>
void foo(T,U)
{
std::cout <<"foo() for integral parameters of same type" <<'\n';
}
foo(1,2);
开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 N 天,点击查看活动详情”