我发现 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
最佳答案
标准草案(T
为 thing
)[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]
...
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-qualifiedstd::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/