在带有转换运算符的初始值设定项的情况下,复制列表初始化的假定行为是什么

What is the supposed behavior of copy-list-initialization in the case of an initializer with a conversion operator?

本文关键字:列表 复制 初始化 是什么 情况下 运算符 转换      更新时间:2023-10-16
class AAA {
public:
AAA() {}
AAA(const AAA&) {}
};
class BBB {
public:
BBB() {}
operator AAA() { AAA a; return a; }
};
int main() {
BBB b;
AAA a = {b};
}

上面的代码是在g++和vc++上编译的,但不是clang++
传统语法AAA a = b;在这三个方面都可以编译。

class AAA {};
class BBB {
public:
BBB() {}
operator AAA() { AAA a; return a; }
};
int main() {
BBB b;
AAA a = {b};
}

上面的代码没有在g++、vc++、clang++中的任何一个上编译。与第一个代码片段的唯一区别是,我删除了AAA的两个用户提供的构造函数
同样,传统语法AAA a = b;在这三个方面都可以编译。

我确信,在带有转换运算符的初始化器的情况下,复制初始化的传统语法是定义良好的。但对于C++11副本列表初始化,我感到困惑。clang是采取正确的操作拒绝初始化,还是g++/vc++采取正确的行动接受初始化(如第一个代码片段所示)?为什么第二个代码片段中所做的如此琐碎的更改会导致显著的不同行为?毕竟,在这种情况下,复制列表初始化和传统的复制初始化之间有什么区别?

编辑:添加第三种情况:

class CCC {};
class AAA {
public:
AAA() {}
AAA(const AAA&) {}
AAA(const CCC&) {}
};

class BBB {
public:
BBB() {}
operator CCC() {CCC c; return c;}
};
int main() {
BBB b;
AAA a = {b};
}

以上代码可用于所有三个编译器。如果最终目标构造函数不是复制构造函数,转换运算符是否有效
在这种情况下,传统语法AAA a = b;无法按预期编译这三个语法,因为传统的副本初始化在到达最终副本构造函数之前最多允许一个级别的用户定义隐式转换(最终目的地只能是副本构造函数)。

乞求对所有这些混乱做出系统的解释。。。

对于第一个http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1673(请参阅上一个测试用例):如果列表初始化只将单个元素传递给某个类X的复制/移动构造函数,则不允许对该单个元素进行用户定义的转换以将其转换为X参数。另请参阅http://llvm.org/bugs/show_bug.cgi?id=12117是什么让Clang实现了这个规则

第二:你在击球http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1467(但不是使用相同类型的对象,而是使用不相关类型的对象)。您的聚合根本不提供BBB类型的数据成员。

对于第三种情况:以上两种情况都不适用,因此列表初始化工作,并调用AAACCC构造函数。= b初始化失败,因为只允许尝试在单个用户定义的转换序列中将b转换为AAA。但在这里,您需要首先转换为CCC,然后再次转换为AAA。对于列表初始化,不存在只进行一次用户定义转换的限制。