Подтвердить что ты не робот

Внутри класса, почему `auto b() → decltype (a()) {}` работает, но `decltype (a()) b() {}` does not?

Рассмотрим следующий код: (Идеал)

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

Это дает мне следующую ошибку:

error: не может вызвать функцию-член 'int S:: a()' без объекта


С другой стороны, этот код компилируется отлично: (Ideone)

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


Почему один пример работает, но другой не удается скомпилировать?

Является ли поведение компилятора полностью правильным в обоих примерах?

Если компилятор прав, то почему стандарт задает такое странное поведение?

4b9b3361

Ответ 1

Так как a является нестатической функцией-членом, a() интерпретируется как (*this).a(). Цитата частично из [expr.prim.general]/3,

Если объявление объявляет функцию-член или шаблон функции-члена класса X, выражение thisявляется prvalue типа "указатель на cv-qualifier-seq X" между необязательным cv-qualifer-seq и концом функция-определение, член-декларатор или декларатор. Он не должен появляться перед необязательным cv-qualifier-seq и он не должен появляться в объявлении статической функции-члена (хотя его тип и значение категория определяются в рамках статической функции-члена, поскольку они находятся в нестатической функции-члене).

Возвращаемый тип возвращается после необязательного cv-qualifier-seq (опущен в ваших примерах, так как S::b не соответствует критериям cv), поэтому this может появиться там, но он не может отображаться раньше.

Ответ 2

Несколько дополнений к @Brian answer:

  • В первом примере a() не преобразуется в (*this).a(). Это преобразование указано в [class.mfct.non-static]/3 и происходит только "в контексте, где this может быть используемый". Без этого преобразования код затем плохо сформирован для нарушения [expr.prim.id]/2:

    Идентификатор, который обозначает нестатический элемент данных или нестатический функция члена класса может использоваться только:

    • как часть доступа к члену класса ([expr.ref]), в котором выражение объекта ссылается на класс-член 63 или класс из этого класса, или

    • чтобы сформировать указатель на член ([expr.unary.op]) или

    • если это id-выражение обозначает нестатический элемент данных и появляется в неовальном операнде.

    используя id-выражение a, которое обозначает нестационарную функцию-член за пределами разрешенных контекстов.

  • Тот факт, что преобразование в доступ к классу не имеет значения, важен, поскольку он делает следующий код действительным:

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

    Если decltype(a) выше были преобразованы в decltype((*this).a), тогда код был бы плохо сформирован.

  • *this имеет специальное исключение из обычного правила, согласно которому объект в доступе к члену класса должен иметь полный тип ([expr.prim.this]/2):

    В отличие от выражения объекта в других контекстах, *this не требуется иметь полный тип для доступа к члену класса ([expr.ref]) вне тела функции-члена.