c++ - clang 和 gcc 为相同的代码生成不同的逻辑。哪个是对的?

标签 c++ language-lawyer c++17

我发现 gcc-8 和 clang-6 产生的逻辑有差异。

这发生在一个真实的代码库中,当我使用 clang 开发时,我使用 gcc 部署。

请告知哪个编译器有错误,以便我可以适本地提交错误。

概要

A可隐式转换为 B A可从 A 构造(复制/移动)和std::initializer_list<B> .

初始化 A 时来自 A&& :

  • clang 选择移动构造函数
  • gcc 选择 initializer_list构造函数。

现场演示:https://coliru.stacked-crooked.com/a/bc50bd8f040d6476

MCVE

#include <initializer_list>
#include <utility>
#include <iostream>

struct thing;

struct thing_ref
{
    thing_ref(thing&& other) : ref_(other) {}
    thing_ref(thing& other) : ref_(other) {}

    thing& ref_;
};

struct thing
{
    thing() {}

    thing(std::initializer_list<thing_ref> things)
    {
        std::cout << "initializer_list path\n";
    }

    thing(thing&& other)
    {
        std::cout << "move path\n";
    }

    thing(thing const& other)
    {
        std::cout << "copy path\n";
    }
};

struct foo
{
    foo(thing t) : mything { std::move(t) } {}
    thing mything;
};

int main()
{
    thing t;

    auto f = foo { std::move(t) };
}

编译器设置:

没有什么特别的,根据 coliru 链接:-std=c++17 -O2

最佳答案

标准草案(Tthing)[dcl.init.list] :

List-initialization is initialization of an object or reference from a braced-init-list. ...

List-initialization of an object or reference of type T is defined as follows:

  • If the braced-init-list contains a designated-initializer-list [does not apply]

  • If T is an aggregate class and [does not apply]

  • Otherwise, if T is a character array [does not apply]

  • Otherwise, if T is an aggregate [does not apply]

  • Otherwise, if the initializer list has no elements [does not apply]

  • Otherwise, if T is a specialization of std::initializer_­list<E> [does not apply]

  • Otherwise, if T is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen through overload resolution [applies]

  • ...

[over.match.list] :

When objects of non-aggregate class type T are list-initialized such that [dcl.init.list] specifies that overload resolution is performed according to the rules in this subclause, overload resolution selects the constructor in two phases:

  • Initially, the candidate functions are the initializer-list constructors ([dcl.init.list]) of the class T and the argument list consists of the initializer list as a single argument. [applies]

  • If no viable initializer-list constructor is found, overload resolution is performed again, where the candidate functions are all the constructors of the class T and the argument list consists of the elements of the initializer list.

If the initializer list has no elements and T has a default constructor, the first phase is omitted. [does not apply]

返回 [dcl.init.list] 以了解 initializer-list constructor 是什么是:

A constructor is an initializer-list constructor if its first parameter is of type std::initializer_­list<E> or reference to possibly cv-qualified std::initializer_­list<E> for some type E, and either there are no other parameters or else all other parameters have default arguments ([dcl.fct.default]).

还有一个方便的注释,重申了结论:

Note: Initializer-list constructors are favored over other constructors in list-initialization

我的结论:

应首先考虑初始化列表构造函数候选者,并在其有效时使用。如thing隐式转换为 thing_ref ,应该是有效的。在我看来,GCC 符合要求。

如果您想初始化具有初始化列表构造函数的类型的对象,但不想使用该构造函数,则不要使用列表初始化,即不要使用大括号初始化列表。

关于c++ - clang 和 gcc 为相同的代码生成不同的逻辑。哪个是对的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54476572/

相关文章:

c++ - 函数重载的规则

c++ - 为什么这个构造函数没有给出不完整的类型错误?

c++ - 空的大小是多少?

c++ - 自动模板参数可以传什么有限制吗?

C++17/C++1z 并行使用 std::for_each

c++ - 在 C++ 中实现返回计算值的最有效方法是什么?

c++ - C vs C++ 性能(用于处理短字符串)

C++ Winsock接收数据会卡死

c++ - 在标准 (C++11) 的哪个地方说余数运算符仅适用于整数类型?

c++ - 将 int 数组转换为 integer_sequence