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

goo*_*era 8 c++ g++ visual-c++ c++11 clang++

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

class BBB {
public:
    BBB() {}
    operator AAA() { AAA a; return a; }
};

int main() {
    BBB b;
    AAA a = {b};
}
Run Code Online (Sandbox Code Playgroud)

上面的代码编译在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};
}
Run Code Online (Sandbox Code Playgroud)

上面的代码不能在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};
}
Run Code Online (Sandbox Code Playgroud)

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

乞求所有这些混乱的系统解释......

Joh*_*itb 5

首先,你要点击http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1673(参见最后一个测试用例):如果列表初始化只将一个元素传递给一个副本/ move某个类的构造函数,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.

对于第三种:上述两种情况都不适用,因此列表初始化起作用并调用CCC构造函数AAA.的= b初始化失败,因为它仅允许尝试转换bAAA在一个单一的用户定义的转换序列.但在这里你需要先转换为CCC然后AAA再转换.对于列表初始化,不存在仅执行一次用户定义转换的限制.