Почему реализация 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, которая не могла бы использовать фигурный список инициализации, и нет никакой пользы от его изменения - так что мы находимся там, где мы есть сегодня.

Другие вопросы по тегам