为什么gcc和clang都会为这个程序产生不同的输出? (转换运算符vs构造函数)
程序:
#include <stdio.h> struct bar_t { int value; template<typename T> bar_t (const T& t) : value { t } {} // edit: You can uncomment these if your compiler supports // guaranteed copy elision (c++17). Either way, it // doesn't affect the output. // bar_t () = delete; // bar_t (bar_t&&) = delete; // bar_t (const bar_t&) = delete; // bar_t& operator = (bar_t&&) = delete; // bar_t& operator = (const bar_t&) = delete; }; struct foo_t { operator int () const { return 1; } operator bar_t () const { return 2; } }; int main () { foo_t foo {}; bar_t a { foo }; bar_t b = static_cast<bar_t>(foo); printf("%d,%d\n", a.value, b.value); }
输出为gcc 7/8:
2,2
输出为叮当 4/5(也适用于gcc 6.3)
1,1
在创buildbar_t
实例时,似乎发生了以下情况:
对于gcc ,它calls foo_t::operator bar_t
然后constructs bar_t with T = int
。
对于clang ,它constructs bar_t with T = foo_t
然后calls foo_t::operator int
哪个编译器在这里正确? (或者如果这是某种forms的未定义行为,那么它们都是正确的)
我相信克朗的结果是正确的。
在用户定义的types之间的bar_t a { foo }
直接列表初始化和static_cast中,目标types的构造函数在源types(C ++ 14 [dcl.init.list] / 3 [expr.static.cast] / 4)。 如果重载parsingfind合适的构造函数,那么就使用它。
当进行重载分解时, bar_t::bar_t<foo_t>(const foo_t&)
是可行的,并且比这个模板的任何实例更好的匹配,从而导致在foo上使用转换运算符。 它也比任何默认声明的构造函数都好,因为它们使用了除foo_t
以外的foo_t
,所以使用了bar_t::bar_t<foo_t>
。
目前编写的代码取决于C ++ 17的保证副本elision; 如果你编译没有C ++ 17的保证副本elision(例如-std=c++14
),那么clang会因为在bar_t b = static_cast<bar_t>(foo);
的复制初始化而拒绝这个代码bar_t b = static_cast<bar_t>(foo);
。