Внутри класса, почему `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;}
};
Почему один пример работает, но другой не удается скомпилировать?
Является ли поведение компилятора полностью правильным в обоих примерах?
Если компилятор прав, то почему стандарт задает такое странное поведение?
Ответы
Ответ 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]) вне тела функции-члена.