(可能不是C ++ 14,可能是库TS)工具make_optional
被定义为(in 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
转型?
答案 0 :(得分:16)
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
。
答案 1 :(得分:14)
这不是make_optional
的新行为;效用函数make_pair
和make_tuple
的行为方式相同。我至少可以看到两个理由:
实际使用未决定的类型实例化模板可能是不可取的或不可能的。
如果T
是一个函数类型,那么,你只是不能在一个类中存储一个函数,句点;但你可以存储一个函数指针(腐朽的类型)。
如果T
是数组类型:由于数组不可复制,因此无法复制生成的类“有缺陷”。对于optional
,value_or
成员函数根本无法编译,因为它返回T
。因此,您根本无法在optional
中使用数组类型。
不衰减类型可能会导致意外行为。
如果参数是字符串文字,我个人希望包含的类型为const char*
而不是const char [n]
。这种衰变发生在大多数地方,为什么不在这里?
如果v
是左值,那么T
将被推导为左值引用类型。我是否真的想要一个包含引用的对,例如,因为其中一个参数是左值?当然不是。
对或元组内的类型或可选或任何不应获取v
的cv资格的类型。也就是说,我们将x
声明为const int
。 make_optional(x)
应该创建optional<const int>
吗?不,不应该;它应该创建optional<int>
。
答案 2 :(得分:6)
看看decay
做了什么:
删除参考限定符 - T&
→T
;没有可选引用,否则您无法重置optional
,这需要可分配。
对于数组类型,删除范围 - T[42]
→T*
;可选的固定范围数组没有那么有用,因为每个数组大小都有不同的类型,并且您不能按值直接传递数组类型,这是value
和value_or
成员函数工作所必需的。
对于函数类型,添加指针 - T(U)
→T(*)(U)
;没有可选的函数引用,原因类似。
否则,删除cv限定符 - const int
→int
;没有可选的const
值,否则您无法重置optional
。