在一个类内部,为什么'auto b() -> decltype(a()) {}'有效,而'decltype(a()) b() {}'不起作用?

Inside of a class, why `auto b() -> decltype(a()) {}` works, but `decltype(a()) b() {}` does not?

本文关键字:decltype 一个 有效 不起作用 内部 auto gt 为什么      更新时间:2023-10-16

请考虑以下代码:(Ideone(

struct S
{
    int a() {return 0;}
    decltype(a()) b() {return 1;}
};

它给了我以下错误:

错误:无法在没有对象的情况下调用成员函数"int S::a((">


另一方面,这段代码编译得很好:(Ideone(

struct S
{
    int a() {return 0;}
    auto b() -> decltype(a()) {return 1;}
};


为什么一个示例有效,而另一个示例无法编译?

这两个示例中的编译器行为是否完全正确?

如果编译器是正确的,那么为什么标准要求这种奇怪的行为呢?

由于a是一个非静态成员函数,因此a()被解释为(*this).a()。部分引自[expr.prim.general]/3,

如果声明声明了类X的成员函数或成员函数模板,则表达式this 是可选的 CV-qualifer-seq 之间的类型为 "指向 cv-qualifier-seq X 的指针"的 prvalue 函数定义、成员声明符或声明符它不应出现在可选的 cv 限定符-seq 之前 并且它不应出现在静态成员函数的声明中(尽管它的类型和值 类别在静态成员函数中定义,因为它们在非静态成员函数中定义(。

尾随返回类型位于可选的 cv 限定符 seq 之后(在您的示例中省略,因为S::b不符合 cv 条件(,因此this可以出现在那里,但它不能出现在之前。

@Brian的答案有一些补充:

  1. 在第一个示例中,a()不会转换为 (*this).a() 。该转换在 [class.mfct.non-static]/3 中指定,并且仅在"可以使用this的上下文中"发生。如果没有此转换,则代码的格式不正确,无法违反 [expr.prim.id]/2:

    表示非静态

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

    • 作为类成员访问 ([expr.ref]( 的一部分,其中对象表达式引用成员的类63 或类 派生自该类,或

    • 形成指向成员的指针 ([expr.unary.op](,或

    • 如果该 id-expression 表示非静态数据成员,并且它出现在未计算的操作数中。

    通过使用 id-expression a ,它表示允许的上下文之外的非静态成员函数。

  2. 不会转换为类成员访问这一事实很重要,因为它使以下代码有效:

    struct A {
        int a;
        decltype(a) b();
    };
    

    如果上面的decltype(a)被转换为decltype((*this).a),那么代码的格式将不正确。

  3. *this对通常的规则有一个特殊豁免,即类成员访问中的对象必须具有完整类型 ([expr.prim.this]/2(:

    与其他上下文中的对象表达式不同,对于成员函数体外部的类成员访问 ([expr.ref](,*this不需要是完整类型。