Средство (вероятно, не 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
конкретно как трансформация?
Общее назначение 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
в конце концов.
Это не новое поведение с make_optional
; функции полезности make_pair
а также make_tuple
вести себя так же. Я вижу по крайней мере две причины сделать это:
Это может быть нежелательно или невозможно на самом деле создать экземпляр шаблона с незадействованными типами.
Если T
это тип функции, ну вы просто не может хранить функцию внутри класса, точка; но ты Можно сохранить указатель на функцию (распавшийся тип).
Если T
это тип массива: результирующий класс будет «дефектным», потому что его нельзя скопировать из-за того, что массивы не могут быть назначены для копирования. За optional
value_or
функция-член не может быть скомпилирована вообще, потому что она возвращает T
, Следовательно, вы не можете иметь тип массива в optional
совсем.
Не гниение типов может привести к неожиданному поведению.
Если аргумент строковый литерал, Я лично ожидал бы, что тип содержит const char*
скорее, чем const char [n]
, Этот распад происходит в большинстве мест, так почему бы и нет?
Если v
тогда это значение T
будет выведен как ссылочный тип lvalue. Я действительно хочу пару, содержащую ссылку, например, только потому, что один из аргументов был lvalue? Конечно нет.
Тип внутри пары, кортежа, необязательный или любой другой не должен приобретать квалификацию cv v
, То есть, скажем, у нас есть x
объявлен как const int
, Должен make_optional(x)
создать optional<const int>
? Нет, это не должно; это должно создать optional<int>
,
Просто посмотри на что decay
делает:
Удаляет ссылочные квалификаторы—T&
→ T
; нет дополнительных ссылок, иначе вы не можете переустановить optional
, который должен быть назначен.
Для типов массивов удаляет экстентT[42]
→ T*
; Необязательные массивы фиксированной длины не очень полезны, потому что каждый размер массива имеет свой тип, и вы не можете напрямую передавать типы массива по значению, необходимому для value
а также value_or
функции-члены для работы.
Для типов функций добавляет указательT(U)
→ T(*)(U)
; нет необязательных ссылок на функции по тем же причинам.
В противном случае удаляет cv-qualifiers—const int
→ int
; нет опционально const
значения, опять же, иначе вы не могли бы переустановить optional
,