为什么 requires-expression 在检查私有成员访问的模板和非模板中表现不同?
Why does requires-expression behave differently in template and not-template for checking private member access?
在下面的代码中classA
有一个私有成员函数f
。我想写一个静态断言来检查这个函数是否可以从当前上下文访问(正如 this question 的评论中所建议的那样)。
有3个相似的案例都是基于requires
-expression:
class A{ void f(); };
// #1: accepted by all
static_assert( ![](auto x){ return requires(decltype(x) a){ a.f(); }; }(A{}) );
// #2: rejected by Clang:
static_assert( ![](auto){ return requires(A a){ a.f(); }; }(nullptr) );
// #3: rejected by all
static_assert( ![](void*){ return requires(A a){ a.f(); }; }(nullptr) );
案例 #3(和 Clang 中的案例 #2)被拒绝,错误为:
error: 'f' is a private member of 'A'
演示:https://gcc.godbolt.org/z/Mxs4P7x8s
为什么 requires-expression 在模板和非模板中的行为不同(案例 #1 和 #3)?
哪个编译器在 #2 中是正确的?
虽然 https://eel.is/c++draft/expr.prim.req.general#1 描述了
A requires-expression provides a concise way to express requirements on template arguments
grammar of a requires-expression
requires-expression:
requires requirement-parameter-list(opt) requirement-body
requirement-body:
{ requirement-seq }
requirement-seq:
requirement
requirement requirement-seq
requirement:
simple-requirement
[...]
simple-requirement:
expression ; // A simple-requirement asserts the validity of an expression.
允许 requires-expressions 包含不一定依赖于模板参数的任意表达式,这意味着以下格式正确:
constexpr bool f() { return requires(int a) { a; }; }
constexpr bool g() { return requires { 0; }; }
static_assert(f());
static_assert(g());
因此,对于在模板化实体外部声明的 requires 表达式,适用与任何表达式相同的规则,因此 #3
被所有编译器正确拒绝(不放弃访问检查控制)在 requires-expressions 中的表达式上下文中)。
#1
也被所有编译器正确实现,根据 https://eel.is/c++draft/expr.prim.req.general#5.sentence-2:
The substitution of template arguments into a requires-expression may result in the formation of invalid types or expressions in its requirements or the violation of the semantic constraints of those requirements. In such cases, the requires-expression evaluates to false; it does not cause the program to be ill-formed.
... as a.f();
在泛型 a
的约束表达式中替换为类型 A
导致无效表达式,因为 f()
是私有的并且不可见。
最后,#2
是根据 https://eel.is/c++draft/expr.prim.req.general#5.sentence-6 的 IFNDR:
If the substitution of template arguments into a requirement would always result in a substitution failure, the program is ill-formed; no diagnostic required.
我们同样可以根据 https://eel.is/c++draft/temp.res.general#6.4 争辩说它是 IFNDR:
The program is ill-formed, no diagnostic required, if:
- [...]
- a hypothetical instantiation of a template immediately following its definition would be ill-formed due to a construct that does not depend on a template parameter, or
因此,#2
由所有编译器正确实现,但当编译器实际诊断 IFNDR 违规时,可以说它总是很好,因此 Clang 的可用性之星。
在下面的代码中classA
有一个私有成员函数f
。我想写一个静态断言来检查这个函数是否可以从当前上下文访问(正如 this question 的评论中所建议的那样)。
有3个相似的案例都是基于requires
-expression:
class A{ void f(); };
// #1: accepted by all
static_assert( ![](auto x){ return requires(decltype(x) a){ a.f(); }; }(A{}) );
// #2: rejected by Clang:
static_assert( ![](auto){ return requires(A a){ a.f(); }; }(nullptr) );
// #3: rejected by all
static_assert( ![](void*){ return requires(A a){ a.f(); }; }(nullptr) );
案例 #3(和 Clang 中的案例 #2)被拒绝,错误为:
error: 'f' is a private member of 'A'
演示:https://gcc.godbolt.org/z/Mxs4P7x8s
为什么 requires-expression 在模板和非模板中的行为不同(案例 #1 和 #3)? 哪个编译器在 #2 中是正确的?
虽然 https://eel.is/c++draft/expr.prim.req.general#1 描述了
A requires-expression provides a concise way to express requirements on template arguments
grammar of a requires-expression
requires-expression: requires requirement-parameter-list(opt) requirement-body requirement-body: { requirement-seq } requirement-seq: requirement requirement requirement-seq requirement: simple-requirement [...] simple-requirement: expression ; // A simple-requirement asserts the validity of an expression.
允许 requires-expressions 包含不一定依赖于模板参数的任意表达式,这意味着以下格式正确:
constexpr bool f() { return requires(int a) { a; }; }
constexpr bool g() { return requires { 0; }; }
static_assert(f());
static_assert(g());
因此,对于在模板化实体外部声明的 requires 表达式,适用与任何表达式相同的规则,因此 #3
被所有编译器正确拒绝(不放弃访问检查控制)在 requires-expressions 中的表达式上下文中)。
#1
也被所有编译器正确实现,根据 https://eel.is/c++draft/expr.prim.req.general#5.sentence-2:
The substitution of template arguments into a requires-expression may result in the formation of invalid types or expressions in its requirements or the violation of the semantic constraints of those requirements. In such cases, the requires-expression evaluates to false; it does not cause the program to be ill-formed.
... as a.f();
在泛型 a
的约束表达式中替换为类型 A
导致无效表达式,因为 f()
是私有的并且不可见。
最后,#2
是根据 https://eel.is/c++draft/expr.prim.req.general#5.sentence-6 的 IFNDR:
If the substitution of template arguments into a requirement would always result in a substitution failure, the program is ill-formed; no diagnostic required.
我们同样可以根据 https://eel.is/c++draft/temp.res.general#6.4 争辩说它是 IFNDR:
The program is ill-formed, no diagnostic required, if:
- [...]
- a hypothetical instantiation of a template immediately following its definition would be ill-formed due to a construct that does not depend on a template parameter, or
因此,#2
由所有编译器正确实现,但当编译器实际诊断 IFNDR 违规时,可以说它总是很好,因此 Clang 的可用性之星。