我尝试做类似于清空boost :: optional< int>的事情,但是当在右侧调用带有空括号的复制赋值运算符(或者很可能是移动赋值op)时,空括号将转换为int然后将该值分配给可选项,因此我最终将变量设置为0,而不是我期望的空值.这是一个示例https://godbolt.org/g/HiF92v,如果我尝试使用std :: experimental :: optional,我会得到我期望的结果(在示例中只需用std :: experimental :: optional替换,你会看到指令变为mov eax,eax).
此外,如果我尝试使用不同的模板参数进行boost可选(非整数类型),一些编译器会编译(使用我期望的行为,这里的示例是http://cpp.sh/5j7n)而其他编译器则没有.因此,即使对于相同的lib,根据模板arg,行为也是不同的.
我想了解这里发生了什么,我知道它与我正在使用C 14功能的一个事实有关,这个库没有考虑到它进入设计.我读了boost / optional标题但是我迷失了细节,我也尝试研究编译后的代码,而没有内联类似的结果.
我正在使用gcc 4.9.2和-std = c 14并提升1.57.
顺便说一句:我知道我应该使用boost :: optional :: reset或boost :: none,但我试图与其余代码库中的语义保持一致.
解决方法
void fun(int) { puts("f int"); } void fun(double) { puts("f double"); } int main() { fun({}); // error }
这会导致编译器错误,因为重载决策是不确定的:double和int同样适合.但是,如果非标量类型发挥作用,情况就不同了:
struct Wrap{}; void fun(int) { puts("f(int)"); } void fun(Wrap) { puts("f(Wrap)"); } int main() { fun({}); // ok: f(int) selected }
这是因为标量是更好的匹配.如果由于某种原因,我想要相同的两个重载,但同时我想有趣({})选择重载乐趣(Wrap),我可以稍微调整一下定义:
template <typename T> std::enable_if_t<std::is_same<int,std::decay_t<T>>::value> fun(T) { puts("f int"); } void fun(Wrap) { puts("f(Wrap)"); }
也就是说,fun(Wrap)保持不变,但是第一个重载现在是一个接受任何T的模板.但是使用enable_if我们约束它,因此它只适用于int类型.所以,这是一个非常“人为”的模板,但它确实起到了作用.如果我打电话:
fun(0); // picks fun(T)
人工模板被选中.但如果我输入:
fun({}); // picks fun(Wrap)
人工模板仍然是模板,因此在这种情况下从不考虑类型推导,并且唯一可见的重载是有趣的(Wrap),因此它被选中.
在std :: optional< T>中使用相同的技巧:它没有来自T的赋值.相反,它有一个类似的人工赋值模板,它接受任何U,但后来被约束,因此T == U.你可以在参考实现here中看到它.
升压::可选< T>已经在C 11之前实现,没有意识到这个’重置成语’.因此,它具有来自T的正常分配,并且在T恰好是标量的情况下,来自T的该分配是优选的.因此差异.
鉴于这一切,我认为Boost.Optional有一个错误,它与std :: optional相反.即使它在Boost.Optional中不可实现,它至少应该无法编译,以避免运行时意外.