为什么gcc和clang分别为此程序产生不同的输出? (转换运算符与构造函数) [英] Why do gcc and clang each produce different output for this program? (conversion operator vs constructor)

查看:159
本文介绍了为什么gcc和clang分别为此程序产生不同的输出? (转换运算符与构造函数)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

程序:

#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

clang 4/5的输出(也适用于gcc 6.3)

output for clang 4/5 (also for gcc 6.3)

1,1

创建bar_t的实例时似乎正在发生以下情况:

It seems that the following is happening when creating the instances of bar_t:

对于 gcc ,依次为calls foo_t::operator bar_tconstructs bar_t with T = int.

对于 clang ,先constructs bar_t with T = foo_t然后calls foo_t::operator int

哪个编译器在这里正确? (或者如果这是某种形式的不确定行为,也许它们都是正确的)

Which compiler is correct here? (or maybe they are both correct if this is some form of undefined behaviour)

推荐答案

我相信clang的结果是正确的.

I believe clang's result is correct.

bar_t a { foo }直接列表初始化和用户定义类型之间的static_cast中,都应在源类型的用户定义转换运算符之前考虑目标类型的构造函数(C ++ 14 [dcl.init.list] /3 [expr.static.cast]/4).如果重载解析找到合适的构造函数,则使用它.

In both bar_t a { foo } direct-list-initialization and in a static_cast between user defined types, constructors of the destination type are considered before user defined conversion operators on the source type (C++14 [dcl.init.list]/3 [expr.static.cast]/4). If overload resolution finds a suitable constructor then it's used.

执行重载解析时,bar_t::bar_t<foo_t>(const foo_t&)是可行的,并且比该模板的任何实例化都更好,这导致在foo上使用强制转换运算符.它也会比任何默认声明的构造函数都要好,因为它们采用的不是foo_t,所以使用bar_t::bar_t<foo_t>.

When doing overload resolution bar_t::bar_t<foo_t>(const foo_t&) is viable and will be a better match than one to any instantiation of this template resulting in the use of the cast operators on foo. It will also be better than any default declared constructors since they take something other than foo_t, so bar_t::bar_t<foo_t> is used.

当前编写的代码取决于C ++ 17保证的复制省略;如果编译时没有C ++ 17的保证复制保留(例如-std=c++14),则由于bar_t b = static_cast<bar_t>(foo);中的复制初始化,clang会拒绝此代码.

The code as currently written depends on C++17 guaranteed copy elision; If you compile without C++17's guaranteed copy elision (e.g. -std=c++14) then clang does reject this code due to the copy-initialization in bar_t b = static_cast<bar_t>(foo);.

这篇关于为什么gcc和clang分别为此程序产生不同的输出? (转换运算符与构造函数)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆