Каков тип возврата «автоматический» при возврате * this в анонимном классе? - PullRequest
35 голосов
/ 09 июля 2020

В этом коде:

struct
{
    auto operator[](const char*)
    {
        return *this;
    }

} m_some_class;

Какой здесь тип auto?

Ответы [ 5 ]

32 голосов
/ 09 июля 2020

Какой здесь тип auto?

Тип decltype(m_some_class) - т.е. возвращаемое значение того же типа, что и переменная m_some_class.

Обратите внимание, что функция вернет копию из *this.

Если вместо этого требуется ссылка на *this, вы можете использовать auto& или, поскольку C + +14, более общий c decltype(auto).

18 голосов
/ 09 июля 2020

Для анонимных структурных типов компилятор внутренне создает имя, а auto в вашем случае возвращает структуру.

Вы можете видеть ниже, что вашей анонимной структуре присвоено имя __anon_1_1 и operator[] функция возвращает объект структуры __anon_1_1. m_some_class - это экземпляр типа __anon_1_1

cppinsights веб-сайт позволяет понять

ваш код

struct
{
    auto operator[](const char*)
    {
        return *this;
    }

}m_some_class;

версия компилятора

struct __anon_1_1
{
  inline __anon_1_1 operator[](const char *)
  {
    return __anon_1_1(*this);
  }
  
  // inline constexpr __anon_1_1() noexcept = default;
  // inline constexpr __anon_1_1(const __anon_1_1 &) noexcept = default;
};

__anon_1_1 m_some_class = __anon_1_1();
10 голосов
/ 09 июля 2020

Строка в данном коде:

return *this;

возвращает структуру m_some_class саму , т.е. тип operator[]:

decltype(m_some_class); // i.e. the type returned is the same as the struct

Также обратите внимание, что это вернет только экземпляр копии структуры, поскольку переданный аргумент не имеет никакого ссылочного оператора. Любые изменения, внесенные в копию структуры, не повлияют на исходную структуру.

Что такое ключевое слово auto?

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

Кроме того, тип, определяемый auto, может варьироваться в зависимости от различных ситуаций. Например:

auto len = vector.size(); // len is now defined as size_t in compile time

В некоторых системах тип len может быть unsigned long, а в моем случае это unsigned long long, здесь вы не можете явно определить, какой квалификатор правильно использовать в это неопределенное место. Здесь мы используем ключевое слово auto.

7 голосов
/ 09 июля 2020

Все стандартные ссылки ниже относятся к N4659: рабочий черновик, выпущенный после Kona за март 2017 г. / DIS C ++ 17 .

TL; DR: вывод типа возвращаемого значения заполнителя

Не имеет значения, что класс является анонимным, поскольку тип возвращаемого значения выводится исключительно из оператора return.

// Denote the type of the anonymous class as 'T'.

// Return type deduced to 'T'
auto operator[](const char*)
{
    return *this;
}

// Return type deduced to 'T&'
auto& operator[](const char*)
{
    return *this;
}

// Return type deduced to 'T&'
decltype(auto) operator[](const char*)
{
    return *this;
}

Подробности и соответствующие стандартные отрывки следуют ниже.

Вывод типа возвращаемого заполнителя: auto (C ++ 11 и новее)

From [expr.unary.op] / 1 [extract, выделение mine]:

[expr.unary.op] / 1 Унарный оператор * выполняет косвенное обращение : выражение, к которому он применяется должен быть указателем на тип объекта или указателем на тип функции , и результатом будет lvalue, относящееся к объекту или функции, на которую указывает выражение. [...]

Таким образом, результатом *this является lvalue, относящееся к объекту, для которого вызывается вызов оператора.

From [dcl. spe c .auto] / 1 и [dcl.spe c .auto] / 2 [извлечение, акцент mine]:

[dcl.spe c .auto] / 1 Спецификаторы типа auto и decltype(auto) используются для обозначения типа заполнителя, который будет будет заменен позже вычетом из инициализатора. [...]

[dcl.spe c .auto] / 2 Тип заполнителя может отображаться с помощью декларатора функции [...] в любом контексте, в котором такой декларатор действителен. [...] Если объявленный тип возвращаемого значения функции содержит тип заполнителя, тип возвращаемого значения функции выводится из неотброшенных return операторов [...].

Из [dcl.type.auto.deduct] / 2 и [dcl.type.auto.deduct] / 4 [извлечение, выделение mine]:

[dcl.type.auto.deduct] / 2 Тип T, содержащий тип заполнителя, и соответствующий инициализатор e, определяются следующим образом:

  • (2.1) для неотброшенного оператора return, который встречается в функции, объявленной с типом возврата, который содержит тип заполнителя, T - объявленный тип возвращаемого значения и e - это операнд оператора возврата. Если оператор return не имеет операнда, то e равно void();
  • [...]

[dcl.spe c .auto] / 4 Если заполнитель является автоматическим определителем типа, выводимый тип T', заменяющий T , определяется с использованием правил для вывода аргументов шаблона. [...]

[Пример:

const auto &i = expr;

Тип i - это выведенный тип параметра u в вызове f(expr) следующего шаблона придуманной функции:

template <class U> void f(const U& u);

- конечный пример]

Таким образом, тип возвращаемого значения функции оператора-члена

auto operator[](const char*)
{
    return *this;
}

анонимного типа, скажем, T, является выведенным типом параметр u в вызове f(*this) следующего шаблона придуманной функции:

template <class U> void f(U u);

где, как указано выше, *this - это lvalue, и возвращаемый тип, таким образом, выводится как T; а именно тип анонимного класса.

Используя тот же аргумент, возврат функции оператора-члена

auto& operator[](const char*)
{
    return *this;
}

анонимного типа, скажем, T, равен T& .

Согласно приведенному выше аргументу не имеет значения, что класс является анонимным, поскольку тип возвращаемого значения выводится исключительно из оператора return.

Выведение типа возвращаемого заполнителя: decltype(auto) (C ++ 14 и новее)

Если бы мы заменили возвращаемый тип заполнителя auto типом заполнителя decltype(auto), то как Тип возвращаемого значения определен.

decltype(auto) operator[](const char*)
{
    return *this;
}

From [dcl.type.auto.deduct] / 5 [извлечение, акцент mine]:

Если заполнителем является спецификатор типа decltype(auto), T должен быть только заполнителем. Тип, выведенный для T, определяется, как описано в [dcl.type. simple], , как если бы e был операндом decltype.

И из [dcl.type.simple] / 4 , [dcl.type.simple] /4.3 применяется [extract]:

Для выражения e тип, обозначенный decltype(e), определяется следующим образом:

  • [...]
  • (4.4) в противном случае, если e - lvalue, decltype(e) - T&, где T - тип e;

как, как указано выше, e (оператор возврата; *this) является lvalue, а [dcl.type.simple] /4.1, [dcl.type.simple] /4.2 и здесь не применяется [dcl.type.simple] /4.3.

Таким образом, тип возвращаемого значения в Пример OP, измененный с использованием типа заполнителя decltype(auto): T&.

2 голосов
/ 09 июля 2020

Это T, где T - безымянный тип класса.

Несмотря на то, что у него нет известного имени, этот тип все еще существует и может быть «использован» с помощью таких механизмов как auto и decltype.

Однако вы, вероятно, захотите auto&.

...