提问人:303 提问时间:10/18/2023 最后编辑:Jan Schultke303 更新时间:10/19/2023 访问量:174
静态成员函数在声明之前在模板化类中是否可见?
Is a static member function visible inside a templated class before it is declared?
问:
静态成员函数是否应该对 的默认构造函数的 requires-clause 可见?C++ 标准对所提供示例的合法性有何看法?b
s
template<auto...>
struct s {
s() requires (s::b()) = default; // clang nope, gcc ok, msvc ok
static constexpr bool b()
{ return true; }
};
static_assert((s<>{}, true));
来自 Clang 的错误消息:
<source>:3:22: error: no member named 'b' in 's<...>'
3 | s() requires (s::b()) = default;
| ~~~^
<source>:7:15: error: static assertion expression is not an integral constant
expression
7 | static_assert((s<>{}, true));
| ^~~~~~~~~~~~~
<source>:7:16: note: non-constexpr constructor 's' cannot be used in a constant
expression
7 | static_assert((s<>{}, true));
| ^
<source>:3:5: note: declared here
3 | s() requires (s::b()) = default;
| ^
答:
程序的行为(格式错误)可以使用 expr.const#5 来理解,它指出:
5. 表达式 E 是一个核心常量表达式,除非按照抽象机器的规则 ([intro.execution]) 对 E 的计算将计算以下其中一项:
5.2 调用未定义的 constexpr 函数;
(强调我的)
并且由于在调用 时,静态函数是未定义的,因此表达式在该点上不是常量表达式。结合使用调用的上下文不是完整的类上下文这一事实,我们得到了提到的错误。s::b()
constexpr
s::b()
类的完整类上下文是
- 函数体 ([dcl.fct.def.general]),
- 默认参数
- noexcept-specifier ([except.spec]),或者
- 默认成员初始值设定项
在类的成员规范中。
因此,该程序格式不正确。
评论
s
s::b
s::b
requires (decltype(s::b()){true})
constexpr
constexpr
constexpr
简而言之,不会找到成员名称,因为它是在命名点之后声明的,并且 requires-clause 不是完整的类上下文。[class.mem.general]/7 定义了一组完整的类上下文,[class.member.lookup]/3 解释了在名称查找期间可以找到哪些类成员:b
b
声明集是在 C 范围内从 C 的类说明符之后立即搜索 N 的结果,如果 P 位于 C 的完整类上下文中,则从 P 进行单次搜索。如果生成的声明集不为空,则子对象集包含 C 本身,并且计算完成。
由于 requires-clause 不是完整的类上下文,因此从命名点开始进行单个搜索。的声明不在该点之前,因此单个搜索 ([basic.lookup.general]/3) 找不到它。b
b
使此分析复杂化的事实是,模板可以包含依赖名称,而这些依赖名称受不同规则的约束。但是,不是依赖名称,因为它的查找上下文引用当前实例化。请参见 [temp.dep.type]/5:s::b
限定名称 ([basic.lookup.qual]) 是依赖于以下情况
- 它是一个 conversion-function-id,其 conversion-type-id 是依赖的,或者
- 它的查找上下文是依赖的,不是当前实例化,或者
- 它的查找上下文是当前实例化,它是 或
operator=
- 它的查找上下文是当前实例化,并且至少有一个依赖基类,并且该名称的限定名称查找未找到任何内容 ([basic.lookup.qual]) 。
在这些情况下,名称查找需要推迟到实例化时间。在这种情况下,由于查找上下文是当前实例化,因此不需要延迟名称查找,除非它找到的声明可能在依赖基类中(在这种情况下,您需要知道模板参数,然后才能知道是否在基类中)。在这种情况下,不是依赖名称,因为没有基类。在 [temp.res.general]/1 下,不会延迟对非依赖名称的名称查找。所以它什么也找不到(因为它不是在完整的类上下文中),并且程序格式不正确。s
b
b
s::b
所以我认为 GCC 和 MSVC 这里有一个错误,尽管因为模板中的名称查找是一个非常棘手的问题,所以我可能遗漏了一些东西。
评论
s
constexpr