Почему make_optional уменьшает свой тип аргумента?

Средство (вероятно, не C ++ 14, возможно, Library TS) make_optional определено (в n3672) как:

template <class T>
constexpr optional<typename decay<T>::type> make_optional(T&& v) {
return optional<typename decay<T>::type>(std::forward<T>(v));
}

Почему необходимо преобразовать тип T (т.е. не просто вернуться optional<T>), и есть ли философское (а также практическое) обоснование использования decay конкретно как трансформация?

15

Решение

Общее назначение decay это взять тип и изменить его так, чтобы он подходил для хранения.

Посмотрите на эти примеры, которые decay делает работу, а remove_reference не будет

auto foo( std::string const& s ) {
if (global_condition)
return make_optional( s );
else
return {};
}

или же

void function() { std::cout << "hello world!\n"; }
auto bar() { return std::make_optional( function ); }

или же

int buff[15];
auto baz() { return std::make_optional( buff ); }

optional<int[15]> было бы очень странным чудовищем — массивы в стиле C не ведут себя хорошо, когда обрабатываются как литералы, вот что optional делает с его параметром T,

Если вы делаете копию данных, const или же volatile Природа источника не имеет значения. И вы можете сделать простое копирование массивов и функций, просто распадаясь на указатели (не обращаясь к std::array или похожие). (теоретически, работа может быть сделана, чтобы сделать optional<int[15]> работать, но было бы много лишних осложнений)

Так std::decay решает все эти проблемы и на самом деле не вызывает проблем, пока вы позволяете make_optional выводить тип аргумента вместо передачи T в прямом смысле.

Если вы хотите пройти в T в буквальном смысле, нет причин использовать make_optional в конце концов.

16

Другие решения

Это не новое поведение с make_optional; функции полезности make_pair а также make_tuple вести себя так же. Я вижу по крайней мере две причины сделать это:

  1. Это может быть нежелательно или невозможно на самом деле создать экземпляр шаблона с незадействованными типами.

    • Если T это тип функции, ну вы просто не может хранить функцию внутри класса, точка; но ты Можно сохранить указатель на функцию (распавшийся тип).

    • Если T это тип массива: результирующий класс будет «дефектным», потому что его нельзя скопировать из-за того, что массивы не могут быть назначены для копирования. За optional value_or функция-член не может быть скомпилирована вообще, потому что она возвращает T, Следовательно, вы не можете иметь тип массива в optional совсем.

  2. Не гниение типов может привести к неожиданному поведению.

    • Если аргумент строковый литерал, Я лично ожидал бы, что тип содержит const char* скорее, чем const char [n], Этот распад происходит в большинстве мест, так почему бы и нет?

    • Если v тогда это значение T будет выведен как ссылочный тип lvalue. Я действительно хочу пару, содержащую ссылку, например, только потому, что один из аргументов был lvalue? Конечно нет.

    • Тип внутри пары, кортежа, необязательный или любой другой не должен приобретать квалификацию cv v, То есть, скажем, у нас есть x объявлен как const int, Должен make_optional(x) создать optional<const int>? Нет, это не должно; это должно создать optional<int>,

13

Просто посмотри на что decay делает:

  • Удаляет ссылочные квалификаторы—T&T; нет дополнительных ссылок, иначе вы не можете переустановить optional, который должен быть назначен.

  • Для типов массивов удаляет экстентT[42]T*; Необязательные массивы фиксированной длины не очень полезны, потому что каждый размер массива имеет свой тип, и вы не можете напрямую передавать типы массива по значению, необходимому для value а также value_or функции-члены для работы.

  • Для типов функций добавляет указательT(U)T(*)(U); нет необязательных ссылок на функции по тем же причинам.

  • В противном случае удаляет cv-qualifiers—const intint; нет опционально const значения, опять же, иначе вы не могли бы переустановить optional,

6
По вопросам рекламы [email protected]