Рассмотрим следующий код :
#include <type_traits>
int main() {
const int& p = 42;
auto v1 = decltype(p){};
static_assert(std::is_same_v<decltype(v1), int>);
decltype(p) v2{};
static_assert(std::is_same_v<decltype(v2), const int&>);
// auto v3 = X(const int&)X {};
}
Тип v1
выводится как int
. При этом тип v2
ожидаемо выводится как const int&
. Я думаю, что первый шаг для v1
можно рассматривать как добавление еще одного псевдонима типа using T = decltype(p);
, а затем auto v4 = T{};
. Как компилятор обрабатывает это выражение (decltype(p){}
или T{}
)? Я знаю, что часть {}
предназначена для создания экземпляров, но как результирующий тип v1
не является ссылочным типом?
Еще вопрос: есть ли способ объявить переменную v3
того же типа, что и v1
, используя явно указанный тип const int&
(вместо decltype(p)
)?
Любые ссылки на стандарт приветствуются.
Отвечает ли это на ваш вопрос? Приведение функционального приведения к ссылочному типу, дыра в стандарте или баг компилятора?
Еще один вопрос 1 вопрос на вопрос плз
const auto& v3 = (const int&){};
отвечает на ваш Yet another question: is there a way to declare v3...?
?
@KamilCuk хм... Я проверил clang, и он не компилируется. Работает в gcc.
@αλεχολυτ, что отличает этот синтаксис (const auto& v3 = (const int&){};
) от const int& v3{};
? Я имею в виду, для чего вам нужен этот синтаксис?
@Enlico, я просто хотел узнать, как воспроизвести decltype(p){}
без decltype
.
(Для отрицательных голосов: если вы проголосовали против, потому что считаете, что цитирование Скотта Мейерса не эквивалентно цитированию стандарта, ну ладно...)
Как вы можете прочитать из Effective Modern C++ (дополненный частью errata, которую вы можете найти, выполнив поиск Case 2:
по этой ссылке, и это просто упрощает следующее чтение, но это не существенно для вопроса):
Если
ParamType
не является ссылкой [...], если типexpr
является ссылкой, игнорируйте часть ссылки. Если [...]expr
этоconst
, примите это во внимание. Если этоvolatile
, также игнорируйте это.
где param
— спецификатор объявления, который в вашем случае просто auto
, то есть не ссылка.
Другими словами, вы создаете v1
с помощью обычного auto
(не auto&
), т. е. путем копирования, поэтому не имеет значения, инициализируете ли вы его сущностью, которая является ссылкой или нет, или даже const
, или нет (или volatile
, или нет, между прочим), потому что вы его копируете.
Подумайте о более простом случае,
int i = 3;
int& p = i;
auto v1 = p;
что касается v1
, на самом деле не важно, инициализировано ли оно одним (i
) или другим (p
) именем, под которым известен один и тот же объект, потому что он получит копию любого значения, которое имеет этот объект.
Вывод типа auto
работает так же, как вывод типа шаблона (за исключением разницы в том, как они работают с инициализатором в фигурных скобках, что не имеет значения в данном случае), и для обоих из них вы можете обратиться к Эффективному современному C++ Скотта Мейерса.
auto v4 = T{}
;. Как компилятор обрабатывает это выражение (decltype(p){}
илиT{}
)? Я знаю, что часть{}
предназначена для создания экземпляров, но почему тип результата не является ссылочным?
Результат decltype(p){}
является ссылочным типом. Использование auto
приводит к удалению const
и квалификаторов ссылок. Вывод типа объясняется здесь, и они такие же, как те, которые используются для вывода типа шаблона. Вместо этого вы можете использовать decltype(auto)
, чтобы сохранить эти квалификаторы (или, в этом конкретном случае, вы можете использовать const auto&
).
Также выпадает volatile
, если есть, как я объяснил в своем ответе.
Сначала не мог поверить, что
decltype(p){}
компилируется. По-видимому, значение{}
инициализирует временное значениеint
, которое затем привязывается к ссылке. Хм!