什么时候需要用括号括起 requires 子句表达式? (一语双关)
When is a requires clause expression required to be parenthesised? (pun by accident)
这给出了一个错误:
template <class T, T A, T B>
requires A > B // <-- error
class X{};
error: parentheses are required around this expression in a requires
clause
requires A < B
~~^~~
( )
几乎所有的运算符都会报这个错误(requires A > B
, requires A == B
, requires A & B
, requires !A
)
但是 &&
和 ||
似乎有效:
template <class T, T A, T B>
requires A && B // <-- ok
class X{};
在 godbolt 上使用 gcc 主干和 clang 主干(2020 年 5 月)进行测试。两个编译器给出相同的结果。
是的,&&
和 ||
在这里被特殊对待,因为约束知道合取和析取。
§ 13.5.1 Constraints [temp.constr.constr]
A constraint is a sequence of logical operations and operands that specifies requirements on template arguments. The operands of a
logical operation are constraints. There are three different kinds of
constraints:
(1.1) — conjunctions (13.5.1.1),
(1.2) — disjunctions
(13.5.1.1), and
(1.3) — atomic constraints (13.5.1.2).
它们需要是为了通过约束定义部分排序。
13.5.4 Partial ordering by constraints [temp.constr.order]
[Note: [...] This partial ordering is used to determine
- (2.1) the best viable candidate of non-template functions (12.4.3),
- (2.2) the address of a non-template function (12.5),
- (2.3) the matching of template template arguments (13.4.3),
- (2.4) the partial ordering of class template specializations (13.7.5.2), and
- (2.5) the partial ordering of function
templates (13.7.6.2).
— end note]
使这段代码起作用的原因:
template <class It>
concept bidirectional_iterator = requires /*...*/;
template <class It>
concept randomaccess_iterator = bidirectional_iterator<It> && requires /*...*/;
template <bidirectional_iterator It>
void sort(It begin, It end); // #1
template <randomaccess_iterator It>
void sort(It begin, It end); // #2
std::list<int> l{};
sort(l.begin(), l.end()); // #A -> calls #1
std::vector<int> v{};
sort(v.begin(), v.end()); // #B -> calls #2
但是对于调用 #B
,即使两个 sort
都可行,因为两个约束(randomaccess_iterator
和 bidirectional_iterator
都满足),sort #2
(带有 randomaccess_iterator
) 的一个被正确选择,因为它 比 sort #1
(带有 bidirectional_iterator
的那个) 更受约束 因为 randomaccess_iterator
包含bidirectional_iterator
:
见
Requires 子句使用特殊的表达式语法来避免certain pathological parsing ambiguities. As noted on cppreference,允许的形式是
- 一个primary expression
- 用运算符
&&
连接的一系列主表达式
- 一系列上述表达式与运算符
||
连接
这给出了一个错误:
template <class T, T A, T B>
requires A > B // <-- error
class X{};
error: parentheses are required around this expression in a requires clause
requires A < B ~~^~~ ( )
几乎所有的运算符都会报这个错误(requires A > B
, requires A == B
, requires A & B
, requires !A
)
但是 &&
和 ||
似乎有效:
template <class T, T A, T B>
requires A && B // <-- ok
class X{};
在 godbolt 上使用 gcc 主干和 clang 主干(2020 年 5 月)进行测试。两个编译器给出相同的结果。
是的,&&
和 ||
在这里被特殊对待,因为约束知道合取和析取。
§ 13.5.1 Constraints [temp.constr.constr]
A constraint is a sequence of logical operations and operands that specifies requirements on template arguments. The operands of a logical operation are constraints. There are three different kinds of constraints:
(1.1) — conjunctions (13.5.1.1),
(1.2) — disjunctions (13.5.1.1), and
(1.3) — atomic constraints (13.5.1.2).
它们需要是为了通过约束定义部分排序。
13.5.4 Partial ordering by constraints [temp.constr.order]
[Note: [...] This partial ordering is used to determine
- (2.1) the best viable candidate of non-template functions (12.4.3),
- (2.2) the address of a non-template function (12.5),
- (2.3) the matching of template template arguments (13.4.3),
- (2.4) the partial ordering of class template specializations (13.7.5.2), and
- (2.5) the partial ordering of function templates (13.7.6.2).
— end note]
使这段代码起作用的原因:
template <class It>
concept bidirectional_iterator = requires /*...*/;
template <class It>
concept randomaccess_iterator = bidirectional_iterator<It> && requires /*...*/;
template <bidirectional_iterator It>
void sort(It begin, It end); // #1
template <randomaccess_iterator It>
void sort(It begin, It end); // #2
std::list<int> l{};
sort(l.begin(), l.end()); // #A -> calls #1
std::vector<int> v{};
sort(v.begin(), v.end()); // #B -> calls #2
但是对于调用 #B
,即使两个 sort
都可行,因为两个约束(randomaccess_iterator
和 bidirectional_iterator
都满足),sort #2
(带有 randomaccess_iterator
) 的一个被正确选择,因为它 比 sort #1
(带有 bidirectional_iterator
的那个) 更受约束 因为 randomaccess_iterator
包含bidirectional_iterator
:
见
Requires 子句使用特殊的表达式语法来避免certain pathological parsing ambiguities. As noted on cppreference,允许的形式是
- 一个primary expression
- 用运算符
&&
连接的一系列主表达式
- 一系列上述表达式与运算符
||
连接