如果巴里的出色回答仍然不清楚,这是我的版本,希望对您有所帮助。
最大的问题是为什么用户定义的转换不是optional<int>
直接初始化的首选:
std::optional<int> my_opt(my_foo);
毕竟有一个构造函数optional<int>(optional<int>&&)
以及用户定义的转换my_foo
to optional<int>
.
原因是构造函数模板,应该在以下情况下激活T
(int
) 可构造自U
and U
两者都不是std::in_place_t
nor optional<T>
,并直接初始化T
从中。事情就是这样,消灭了optional(foo&)
.
最终生成的optional<int>
看起来像:
class optional<int> {
. . .
int value_;
. . .
optional(optional&& rhs);
optional(foo& rhs) : value_(rhs) {}
. . .
optional(optional&&)
需要用户定义的转换,而optional(foo&)
完全匹配my_foo
。所以它赢了,直接初始化int
from my_foo
。只有此时operator int()
选择更好的匹配来初始化int
。结果就变成了2
.
2) 如果出现以下情况my_opt = static_cast<std::optional<int>>(my_foo)
,虽然它sounds like "初始化my_opt
就好像它是std::optional<int>
”,其实means "创建一个临时的std::optional<int>
from my_foo
并从中移动分配” 如中所述[expr.static.cast]/4 http://eel.is/c++draft/expr.static.cast#4:
If T
是引用类型,效果与执行相同
声明和初始化
T t(e);
对于一些临时发明的
多变的t
([dcl.init]),然后使用临时变量作为
转换的结果。否则,结果对象是
直接初始化自e
.
所以就变成了:
my_opt = std::optional<int>(my_foo);
我们又回到了之前的情况;my_opt
随后从临时初始化optional
,已经持有2
.
转发引用的重载问题是众所周知的。斯科特·迈尔斯在他的书中有效的现代 C++第 26 章广泛讨论了为什么重载“通用引用”是一个坏主意。这样的模板会不知疲倦地消除您扔给它们的任何类型,这将使所有内容和任何不完全匹配的内容黯然失色。所以我很惊讶委员会选择了这条路线。
至于原因为什么会这样,在提案中N3793 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3793.html#optional.object.ctor并以标准单位表示2016 年 11 月 15 日 https://github.com/cplusplus/draft/commit/7988d9b5e98875b79ae942c92cb32f9990acd02a确实是
optional(const T& v);
optional(T&& v);
但随后作为LWG 缺陷 2451 http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2451它被改为
template <class U = T> optional(U&& v);
理由如下:
如下所示的代码目前格式不正确(感谢 STL
令人信服的例子):
optional<string> opt_str = "meow";
这是因为它需要两个用户定义的转换(从const char*
to string
,并从string
to optional<string>
)其中
语言只允许一种。这可能会是一个惊喜和
给用户带来不便。
optional<T>
应该可以从任何隐式转换U
那是
隐式转换为T
。这可以作为非显式实现
构造函数模板optional(U&&)
,仅通过 SFINAE 启用
如果is_convertible_v<U, T>
and is_constructible_v<T, U>
,加上任意
需要附加条件以避免与其他条件产生歧义
构造函数...
最后我认为没关系T
排名高于optional<T>
,毕竟这是一个相当不寻常的选择may有一个值并且the value.
从性能角度来看,从以下位置进行初始化也有好处:T
而不是来自另一个optional<T>
. An optional
通常实现为:
template<typename T>
struct optional {
union
{
char dummy;
T value;
};
bool has_value;
};
所以初始化它optional<T>&
看起来像
optional<T>::optional(const optional<T>& rhs) {
has_value = rhs.has_value;
if (has_value) {
value = rhs.value;
}
}
而初始化从T&
需要更少的步骤:
optional<T>::optional(const T& t) {
value = t;
has_value = true;
}