有没有理由我们无法在未评估的上下文中命名非静态成员函数?

Sto*_*ica 9 c++ language-lawyer

阅读[expr.prim.id]时,会看到这一点

只能使用表示非静态数据成员或类的非静态成员函数的id表达式:

  • 如果该id-expression表示非静态数据成员,则它出现在未评估的操作数中.

上面的子弹仅适用于数据成员这一事实对我来说并不清楚.直觉上我希望以下内容形成良好:

#include <type_traits>

using func = int();

class bar {
  func foo; // This is valid, and not the subject of the question
};

static_assert(std::is_same<decltype(bar::foo), func>::value, "No Symmetry!");
Run Code Online (Sandbox Code Playgroud)

decltype()即使在检查静态断言之前,它也是不正确的.

是否有一些我不知道的歧义?

Sto*_*ica 3

我是否缺少一些含糊之处?

事实上,有大量类型信息作为成员函数声明的一部分添加。

虽然func肯定可以用来声明该成员,但故事并没有到此结束。一旦声明了成员,它的类型就完成了。这涉及添加一些其他内容,例如cv-qualifiersref-qualifiers。在 的情况下foo,所有默认的隐式都被确定,并且它们成为 的bar::foo类型的一部分。如[dcl.fct]/8所指定:

返回类型、参数类型列表、引用限定符、cv 限定符序列和异常规范(但不是默认参数)是函数类型的一部分。

没有办法在上面的声明中明确指定它们foo(尽管它们可以添加到func),但通常可以添加它们:

class bar {
  int foo() const volatile &&;
};
Run Code Online (Sandbox Code Playgroud)

它们是函数类型的一部分,decltype(bar::foo)如果它们出现,就应该解决它们(如果我正确收集,即使它们没有)。

const volatile &&当我们尝试评估时,会去哪里decltype(bar::foo)

  • 是否应该被忽略?这是可以做到的。但丢失类型信息很少是一件好事。
  • 我们是否应该保留它,并将该类型decltype计算为指向成员函数的指针?
    这也可以,但现在它与数据成员在未评估的上下文中命名时的行为不同。我们引入一个差异。
  • 是否应该保留它,并将类型解析为其他类型?也许类似int(foo const volatile&&)or int() const volatile &&(函数类型的另一种形式)?这打破了人们所期望的对称性,并且又与数据成员产生了差异。

没有任何简单或明显的方法可以让其始终有效。因此,与其让使用有限的功能变得复杂,不如将其视为格式不正确。

  • 我不知道,我想我同意 Quentin 的观点,如果 `decltype(bar::foo)` 确实有效,那么它是“显而易见的”,你的例子应该导致 `int()const volatile&amp;&amp;`。(如果“foo”被重载,程序就会格式不正确。)我更认为它被认为没有足够的有用性来指定和实现。 (4认同)
  • 你的意思是没有`func foo const;`?好吧,但是这会是阻止 `decltype(bar::foo)` 生成完全限定的函数类型(即示例中的 `int() const volatile &amp;&amp;` )的基本原理吗? (2认同)
  • 这是有争议的:数据成员的 `decltype` 被专门定义为其“基本”类型(就好像它是一个自由变量),但随后采用 `&amp;Foo::bar` 将产生不同的类型,具体取决于数据成员 ` bar` 是否为“static”。也许我很困惑,但对我来说,数据成员的“decltype”对称性可以通过返回合格的函数类型来实现,即使该类型是“令人厌恶的”,因此几乎不可用。 (2认同)