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

Почему здесь нет фигурных скобок ни в одной из реализаций С++ 11/14? Другими словами, почему бы и нет

 template <class... Types>
    std::tuple<special_decay_t<Types>...> make_tuple(Types&&... args)
    {
        return {std::forward<Types>(args)...};
    }

person Patrick Fromberg    schedule 31.01.2018    source источник
comment
Вы не можете сделать это с типом возврата auto.   -  person aschepler    schedule 31.01.2018
comment
@aschepler, да, я знаю, но почему это проблема?   -  person Patrick Fromberg    schedule 31.01.2018
comment
Список инициализации в фигурных скобках не имеет типа, поэтому тип возвращаемого значения не может быть выведен.   -  person Praetorian    schedule 31.01.2018
comment
@Praetorian, да, я знаю, возвращаемый тип не может быть автоматическим, почему он должен быть автоматическим?   -  person Patrick Fromberg    schedule 31.01.2018
comment
Поскольку список инициализаторов в фигурных скобках сам по себе не считается инициализатором для std::tuple. Не могли бы вы создать правило, согласно которому список инициализации в фигурных скобках для auto приводит к std::tuple? Можно, но в C++ такого правила нет.   -  person Sam Varshavchik    schedule 31.01.2018
comment
О, вы спрашиваете, почему auto не заменяется на std::tuple<special_decay_t<Types>...>, а затем в операторе return используется список инициализации в фигурных скобках. Вы должны отредактировать вопрос, чтобы уточнить это.   -  person Praetorian    schedule 31.01.2018
comment
@Praetorian, я только что сделал, но именно поэтому я начал с «пожалуйста, прочитайте». . . что должно было прояснить.   -  person Patrick Fromberg    schedule 31.01.2018
comment
@Praetorian, я сделал, и это не так. Я просто не понимаю этого комментария.   -  person Patrick Fromberg    schedule 31.01.2018
comment
Я прочитал вопрос, на который вы ссылались, и мне все еще не ясно, о чем вы спрашивали.   -  person Praetorian    schedule 31.01.2018
comment
Я думаю, что ваш текст недостаточно подчеркивает комбинации, поскольку он не доносит эту идею до ваших читателей в первых абзацах. Второй абзац должен был установить упоминание комбинации auto и return tuple... и комбинации tuple и return {...}, а последующие абзацы могли относиться к комбинациям.   -  person    schedule 31.01.2018


Ответы (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 конструктор перемещения или копирования, возврат braced-init-list не работает.


Что еще более важно, поскольку T.C. указывает, пока Улучшение 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, которая не могла бы использовать braced-init-list, и нет никакой пользы от ее изменения — так что мы находимся там, где мы есть сегодня. .

person Barry    schedule 31.01.2018
comment
Не забывайте, что этот конструктор полностью явный, пока не будет улучшена пара и кортеж. - person T.C.; 31.01.2018
comment
@Т.С. Я забыл! - person Barry; 31.01.2018