Bar*_*rry 155 c++ c++-concepts c++20
C++ 20概念的一个角落是,在某些情况下你必须编写requires requires.例如,[expr.prim.req]/3中的这个例子:
甲需要表达也可以在使用需要子句([温度])作为写在模板参数特设约束,如下面的一个的一种方法:
Run Code Online (Sandbox Code Playgroud)template<typename T> requires requires (T x) { x + x; } T add(T a, T b) { return a + b; }第一个需要引入requires子句,第二个需要引入requires-expression.
需要第二个requires关键字的技术原因是什么?为什么我们不能只允许写作:
template<typename T>
requires (T x) { x + x; }
T add(T a, T b) { return a + b; }
Run Code Online (Sandbox Code Playgroud)
(注意:请不要回答那个语法requires吧)
Nic*_*las 77
这是因为语法需要它.确实如此.
一个requires约束不具有使用requires表达式.它可以使用任何或多或少的任意布尔常量表达式.因此,requires (foo)必须是合法的requires约束.
甲requires 表达是一种独特的构建体(即测试某些事情是否遵循一定的约束条件的东西); 它只是由相同的关键字引入.requires (foo f)将是有效requires表达式的开始.
你想要的是,如果你requires在一个接受约束的地方使用,你应该能够从该requires子句中创建一个"约束+表达式" .
所以这里有一个问题:如果你requires (foo)进入一个适合需求约束的地方......解析器必须走多远才能意识到这是一个需要约束而不是约束+表达你想要它的方式成为?
考虑一下:
void bar() requires (foo)
{
//stuff
}
Run Code Online (Sandbox Code Playgroud)
如果foo是类型,那么(foo)是需要表达式的参数列表,并且其中的所有内容{}都不是函数的主体,而是该requires表达式的主体.否则,foo是requires子句中的表达式.
好吧,你可以说编译器应该只知道什么foo是第一个.但是,当解析一系列令牌的基本行为要求编译器在识别令牌之前弄清楚这些标识符的含义时,C++ 真的不喜欢它.是的,C++是上下文敏感的,所以这确实发生了.但委员会倾向于尽可能避免它.
是的,这是语法.
Quu*_*one 58
情况完全类似于noexcept(noexcept(...)).当然,这听起来更像是一件好事,而不是一件好事,但让我解释一下.:)我们将从你已经知道的东西开始:
C++ 11有" noexcept-clauses"和"-expressions" noexcept.他们做不同的事情.
A noexcept-clause说," 当......(某些情况)时,这个功能应该是不存在的." 它继续进行函数声明,获取布尔参数,并在声明的函数中引起行为更改.
A 表达式noexcept说,"编译器,请告诉我(某些表达式)是否为noexcept." 它本身就是一个布尔表达式.它对程序的行为没有"副作用" - 它只是向编译器询问是/否问题的答案."这个表达是不是没有?"
我们可以在noexcept-clause中嵌套noexcept-expression,但我们通常认为这样做是不好的.
template<class T>
void incr(T t) noexcept(noexcept(++t)); // NOT SO HOT
Run Code Online (Sandbox Code Playgroud)
将noexcept-expression 封装在类型特征中被认为是更好的风格.
template<class T> inline constexpr bool is_nothrow_incrable_v =
noexcept(++std::declval<T&>()); // BETTER, PART 1
template<class T>
void incr(T t) noexcept(is_nothrow_incrable_v<T>); // BETTER, PART 2
Run Code Online (Sandbox Code Playgroud)
C++ 2a工作草案有" requires-clauses"和"-expressions" requires.他们做不同的事情.
A requires-clause说,"这个函数应该在......(某些条件)时参与重载解析." 它继续进行函数声明,获取布尔参数,并在声明的函数中引起行为更改.
A 表达式requires说:"编译器,请告诉我(某些表达式)是否格式正确." 它本身就是一个布尔表达式.它对程序的行为没有"副作用" - 它只是向编译器询问是/否问题的答案."这个表达是否良好?"
我们可以在requires-clause中嵌套requires-expression,但我们通常认为这样做是不好的.
template<class T>
void incr(T t) requires (requires(T t) { ++t; }); // NOT SO HOT
Run Code Online (Sandbox Code Playgroud)
将requires-expression 封装在类型特征中被认为是更好的风格......
template<class T> inline constexpr bool is_incrable_v =
requires(T t) { ++t; }; // BETTER, PART 1
template<class T>
void incr(T t) requires is_incrable_v<T>; // BETTER, PART 2
Run Code Online (Sandbox Code Playgroud)
......或(C++ 2a Working Draft)概念.
template<class T> concept Incrable =
requires(T t) { ++t; }; // BETTER, PART 1
template<class T>
void incr(T t) requires Incrable<T>; // BETTER, PART 2
Run Code Online (Sandbox Code Playgroud)
The*_*ist 15
我认为cppreference的概念页面解释了这一点.我可以用"数学"解释一下,为什么这个必须是这样的:
如果要定义概念,请执行以下操作:
template<typename T>
concept Addable = requires (T x) { x + x; }; // requires-expression
Run Code Online (Sandbox Code Playgroud)
如果要声明使用该概念的函数,请执行以下操作:
template<typename T> requires Addable<T> // requires-clause, not requires-expression
T add(T a, T b) { return a + b; }
Run Code Online (Sandbox Code Playgroud)
现在,如果你不想单独定义这个概念,我想你所要做的就是一些替代.取这部分requires (T x) { x + x; };并更换Addable<T>部件,你会得到:
template<typename T> requires requires (T x) { x + x; }
T add(T a, T b) { return a + b; }
Run Code Online (Sandbox Code Playgroud)
这就是你要问的问题.
Bar*_*rry 10
我发现评论从安德鲁·萨顿(观念作者之一,谁实现了它在海湾合作委员会),以在这方面非常有帮助的,所以我想我只是引用它在这里的大致整体:
不久前,在约束表达式(第一个要求引入的短语)中不允许使用表达式(第二个要求引入的短语).它只能出现在概念定义中.实际上,这正是该论文中出现该声明的部分所提出的内容.
然而,在2016年,有一项提议放宽该限制[编者注:P0266 ].请注意该文件第4节第4段的删除线.因此诞生需要.
说实话,我从来没有在GCC中实际实施过这种限制,所以它一直是可能的.我认为沃尔特可能已经发现了这一点,并发现它很有用,导致了这篇论文.
为了避免任何人认为我对写作不敏感需要两次,我确实花了一些时间来确定是否可以简化.简答:不.
问题是需要在模板参数列表之后引入两种语法结构:非常常见的是约束表达式(如
P && Q)和偶尔的语法要求(如requires (T a) { ... }).这被称为需求表达式.第一个要求引入约束.第二个要求介绍requires-expression.这就是语法组成的方式.我觉得它根本不会让人感到困惑.
我曾试图将这些折叠成一个单一的要求.不幸的是,这会导致一些非常困难的解析问题.例如,在需求
(之后是否表示嵌套的子表达式或参数列表时,您无法轻易判断.我不相信这些语法有完美的消歧(参见统一初始化语法的基本原理;这个问题也存在).所以你做出了一个选择:make需要引入一个表达式(就像现在一样)或者让它引入一个参数化的需求列表.
我选择当前的方法是因为大部分时间(在近100%的时间内),我想要的不是需求表达式.在非常罕见的情况下,我确实想要一个针对临时约束的需求表达式,我真的不介意两次写这个单词.这是一个明显的指标,我没有为模板开发出足够完善的抽象.(因为如果我有,它会有一个名字.)
我本可以选择使需求引入一个require-expression.这实际上更糟糕,因为几乎所有的约束都会开始如下所示:
Run Code Online (Sandbox Code Playgroud)template<typename T> requires { requires Eq<T>; } void f(T a, T b);这里,第二个要求称为嵌套要求; 它评估其表达式(不评估requires-expression的块中的其他代码).我认为这比现状更糟糕.现在,你可以在任何地方写两次.
我也可以使用更多关键字.这本身就是一个问题 - 而且不只是自行车脱落.可能有一种方法可以"重新分配"关键字以避免重复,但我没有给出那种严肃的想法.但这并没有真正改变问题的本质.
| 归档时间: |
|
| 查看次数: |
10826 次 |
| 最近记录: |