Clang 拒绝以下代码:
#include <concepts>
struct k { static void f(); };
// clang nope, gcc ok, msvc ok
static_assert(std::same_as<decltype(k::f), decltype(k{}.f)>);
这似乎可以归结为是否decltype(k{}.f)
应将其检查为类型void()
、类型void (&)()
甚至类型void (&&)()
。当使用括号k{}.f
(即decltype((k{}.f))
:)时,Clang 和 GCC 都同意类型void (&)()
,而 MSVC 将表达式视为类型void (&&)()
。这似乎表明 GCC 和 MSVC 都不将 plain 的类型检查视为k{}.f
左值表达式。
decltype
我推测在检查 形式的静态成员函数时没有特殊例外k{}.f
。因此,这是否意味着 GCC 和 MSVC 都不符合要求?
5
最佳答案
1
这些decltype 说明符受管辖:
否则,如果E是未带括号的id 表达式或未带括号的类成员访问 ([expr.ref]),则
decltype(
E是E)
命名的实体的类型。如果不存在这样的实体,则程序格式不正确;
k::f
是一个不带括号的id 表达式,并且它命名的函数具有类型void()
。
那么k{}.f
,不带括号的类成员访问呢?此表达式受约束:
如果
E2
是重载集,则表达式应为成员函数调用 ([expr.call]) 的(可能带括号的)左操作数,并且函数重载解析 ([over.match]) 用于选择所E2
引用的函数。 的类型E1.E2
是 的类型E2
,并E1.E2
引用 所引用的函数E2
。[…]
重点是我加的。粗体字眼k{}.f
在不用于调用函数时使表达式格式不正确k::f
。请注意,即使f
查找只找到一个名为的函数,它仍被视为重载集()。
)将此种表达式标记为格式错误。似乎还没有编译器实现它。这个核心问题的动机可能是围绕处理此类表达式的实现分歧。
在添加粗体措辞之前,末尾的措辞将决定结果:E1.E2
指的是 所引用的函数E2
,即函数k::f
,因此您将再次得到void()
类型。
5
-
保留“如果 E2 引用静态成员函数,则 E1.E2 是左值”这句话有什么意义呢?
– -
1@Barry 据我所知,我们所说的值类别是什么并不重要。但是,我们确实在 [basic.lval] 中声明每个表达式都是左值、右值或纯右值。
– -
DR 建议修改标准中的措辞,以明确表示如果这是预期结果
void (*q)() = y.f;
,则表达式应该是格式错误的。这似乎表明有空间决定不将此类表达式视为错误。也可以通过更改工作方式以允许此类表达式来解决实现分歧。那么,最终拒绝这一决定的理由是什么?
– -
@303 我认为有些人认为之前的措辞已经禁止了这种行为,在这种情况下,CWG2725 中的更改仅仅是一种澄清。
– -
@BrianBi 他们选择这条路确实有点奇怪,因为从元编程的角度来看,现在事情变得有点笨拙。例如:
template<auto K> decltype((K.f)) g();
现在必须变成:template<auto K> decltype((decltype(K)::f)) g();
。我觉得有一些余地可以争论说,这种变化使事情变得更加复杂,而没有真正的好处。
–
|
–
k::f
对于非静态成员函数来说,这是无效语法。Clangs 似乎将这种无效性扩展到了静态成员函数。因此,decltype(k::f)
无法推断。可能是一个错误。–
–
decltype(k::f)
无法推断?所有编译器似乎都将其推断为类型void()
。–
k::f
在 C++ 中,如果 id 表达式是非f
静态成员函数,则为无效。&k::f
是有效的。–
|