为什么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);