clang和gcc为相同的代码生成不同的逻辑.哪个是对的?

Ric*_*ges 12 c++ language-lawyer c++17

我发现gcc-8和clang-6产生的逻辑之间存在差异.

这发生在真正的代码库中,当使用clang开发时,我使用gcc进行部署.

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

概要

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

初始化时AA&&:

  • clang选择move-constructor
  • 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) };
}
Run Code Online (Sandbox Code Playgroud)

编译器设置:

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

eer*_*ika 6

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

列表初始化是从braced-init-list初始化对象或引用....

列表初始化对象或类型T的引用定义如下:

  • 如果braced-init-list包含指定的初始化列表[不适用]

  • 如果T是一个聚合类并且[不适用]

  • 否则,如果T是一个字符数组[不适用]

  • 否则,如果T是聚合[不适用]

  • 否则,如果初始化列表没有元素[不适用]

  • 否则,如果T是[不适用]的专业化std::initializer_­list<E>

  • 否则,如果T是类类型,则考虑构造函数.枚举适用的构造函数,并通过重载决策选择最佳构造函数[apply]

  • ...

[over.match.list]:

当非聚合类类型T的对象被列表初始化,使得[dcl.init.list]指定根据本子条款中的规则执行重载解析时,重载决策分两个阶段选择构造函数:

  • 最初,候选函数是类T 的初始化列表构造函数([dcl.init.list]),参数列表由初始化列表作为单个参数组成.[适用]

  • 如果找不到可行的初始化列表构造函数,则再次执行重载解析,其中候选函数是类T的所有构造函数,参数列表由初始化列表的元素组成.

如果初始化列表没有元素且T具有默认构造函数,则省略第一个阶段.[不适用]

返回[dcl.init.list]以找出初始化列表构造函数是什么:

构造函数是初始化列表构造函数,如果它的第一个参数是某个类型E 的类型std::initializer_­list<E>或引用可能是cv-qualified std::initializer_­list<E>,并且没有其他参数或者所有其他参数都有默认参数([dcl.fct.default] ).

还有一个方便的说明,重申结论:

注意:初始化列表构造函数优于列表初始化中的其他构造函数

我的结论:

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

如果要初始化具有初始化列表构造函数但不想使用该构造函数的类型的对象,则不要使用列表初始化,即不要使用brace-init-list.