Почему реализация make_tuple не возвращается через инициализацию фигурной скобки?
Чтобы понять вопрос, сначала прочтите этот ответ.
Я проверил разные исторические make_tuple
реализации (в том числе Clang версии 2012 года). До C++17 я бы ожидал, что они return {list of values ... }
но все они создают кортеж, прежде чем возвращать его. Все они соответствуют очень упрощенному текущему примеру cppreference:
template <class... Types>
auto make_tuple(Types&&... args)
{
return std::tuple<special_decay_t<Types>...>(std::forward<Types>(args)...);
}
Это не так, но смысл возврата инициализации скобки состоит в том, чтобы создать возвращаемый объект напрямую. До C++17 не было гарантированного исключения копирования, которое удаляет временные объекты даже концептуально. Но даже с C++17 не обязательно ожидать исчезновения фигурных скобок в этом примере.
Почему здесь нет фигурных скобок ни в одной из реализаций C++ 11/14? Другими словами, почему бы и нет
template <class... Types>
std::tuple<special_decay_t<Types>...> make_tuple(Types&&... args)
{
return {std::forward<Types>(args)...};
}
1 ответ
Преимущество, о котором вы говорите, просто не применимо в этом случае. Да, вы можете создать неподвижный тип следующим образом:
struct Nonmovable {
Nonmovable(int );
Nonmovable(Nonmovable&& ) = delete;
};
Nonmovable foo() { return {42}; } // ok
Но в контексте make_tuple
все элементы должны быть перемещаемыми или копируемыми в любом случае, потому что вы собираетесь перемещать или копировать их в фактический tuple
что вы строите:
std::tuple<Nonmovable> make_tuple(Nonmovable&& m) {
return {std::move(m)}; // still an error
}
Так что на самом деле нет никакого преимущества:
template <class... Types>
std::tuple<special_decay_t<Types>...> make_tuple(Types&&... args)
{
return {std::forward<Types>(args)...};
}
над
template <class... Types>
auto make_tuple(Types&&... args)
{
return std::tuple<special_decay_t<Types>...>(std::forward<Types>(args)...);
}
В этом смысле.
Да, согласно языку стандарта, одно из них подразумевает создание непосредственно в возвращаемом объекте, а другое предполагает временный. Но на практике каждый компилятор оптимизирует это. Один случай, когда мы не могли этого знать, не применим - наш tuple
должен быть подвижным.
Есть по крайней мере один странный недостаток использования {...}
здесь, что на случайный тип имеет explicit
Переместить или скопировать конструктор, возвращение списка фигурных скобок не работает.
Что еще более важно, как указывает TC, до улучшения pair
а такжеtuple
был принят, конструктор дляstd::tuple
был всегда explicit
,
// in C++11
explicit tuple( const Types&... args );
// in C++14
explicit constexpr tuple( const Types&... args );
// in C++17, onwards
/*EXPLICIT*/ constexpr tuple( const Types&... args );
Что сделало реализацию, возвращающую список в скобках, невозможной. Таким образом, каждая библиотека уже имела make_tuple()
реализация до C++17, которая не могла бы использовать фигурный список инициализации, и нет никакой пользы от его изменения - так что мы находимся там, где мы есть сегодня.