想象我们有:
struct S {
struct S {
S() { printf("%s\n", __PRETTY_FUNCTION__); }
S(const S&) { printf("%s\n", __PRETTY_FUNCTION__); }
S(S&&) { printf("%s\n", __PRETTY_FUNCTION__); }
~S() { printf("%s\n", __PRETTY_FUNCTION__); }
S(std::initializer_list<S>) { printf("%s\n", __PRETTY_FUNCTION__); }
};
S s2{S{}};
时应调用哪个构造函数?gcc和clang具有不同的行为可以吗?
示例:https://godbolt.org/z/qQxyp5
gcc(trunk)输出:
S::S()
S::S(std::initializer_list<S>)
S::~S()
S::~S()
c(干)输出:
S::S()
S::~S()
这里的GCC是正确的;列表初始化不允许在C ++ 17中进行复制省略。
如果您已完成S s2(S{});
,则由于S
,仅调用[dcl.init]/17.6.1的默认构造函数才需要这样做:
如果初始化程序表达式是prvalue,并且源类型的cv不合格版本与目标程序的类相同,则使用初始化程序表达式来初始化目标对象。 [示例:T x = T(T(T()));调用T的默认构造函数初始化x。 —示例]
但是,这仅适用于副本初始化和直接初始化。
执行S s2{S{}};
是列表初始化,它是它自己的完全独立的初始化形式,具有自己的规则。由于S
不是聚合,因此[dcl.init.list]3.6将接管,这表示:
否则,如果T是类类型,则考虑构造函数。枚举适用的构造函数,并通过重载分辨率([over.match],[over.match.list])选择最佳的构造函数。
调用构造函数意味着使用一组特定的参数来调用特定的函数。这意味着必须使用prvalue S{}
来初始化所选构造函数的参数。这意味着您have调用复制/移动构造函数。
也不允许常规的,无保证的省略。 [class.copy.elision] / 1给出了在3种情况下允许进行省略的情况:return localVariableName
,throw localVariableName
和catch(TypeName)
(如果捕获的内容与抛出的内容匹配)。这种情况显然不是这些情况,因此它不符合常规省略的条件。