What is the supposed behavior of copy-list-initial

2020-02-09 11:58发布

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

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

int main() {
    BBB b;
    AAA a = {b};
}

The above code compiles on g++ and vc++, but not clang++.
The traditional syntax AAA a = b; compiles ok on all three.

class AAA {};

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

int main() {
    BBB b;
    AAA a = {b};
}

The above code doesn't compile on any of g++, vc++, clang++. The only difference against the first code snippet is that I removed the two user-provided constructors of AAA.
Again, the traditional syntax AAA a = b; compiles ok on all three.

I'm quite sure that the traditional syntax of copy-initialization is well-defined in the case of an initializer with a conversion operator. But for the C++11 copy-list-initialization, I'm confused. Is clang taking the correct action rejecting the initialization or g++/vc++ taking the correct action accepting the initialization (as seen in the first code snippet)? And why such a trivial change as done in the second code snippet will result in that significant different behavior? What's the difference between copy-list-initialization and traditional copy-initialization in this case after all?

EDIT: Adding a third case:

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};
}

The above code compiles for all three compilers. The conversion operator works if the final destination constructor is not the copy constructor?
In this case, the traditional syntax AAA a = b; fails to compile for all three as expected, since traditional copy-initialization allows one level of user-defined implicit conversion at most before arriving at the final copy constructor (the final destination can only be the copy constructor).

Begging for a systematic explanation for all these mess...

1条回答
叛逆
2楼-- · 2020-02-09 12:19

For the first, you are hitting http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1673 (see the last testcase): If list initialization passes only a single element to a copy/move constructor of some class X, user defined conversions are not allowed on that single element to convert it to the X parameter. Also see http://llvm.org/bugs/show_bug.cgi?id=12117 which made Clang implement this rule

For the second: You are hitting http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1467 (but instead of using an object of the same type, you use an object of an unrelated type). Your aggregate simply doesn't provide a data member of type BBB.

For the third: Neither of the above two situations apply, so the list initialization works and calls the CCC constructor of AAA. The = b initialization fails because it is only allowed to try converting the b to an AAA in a single user defined conversion sequence. But here you would need to first convert to CCC and then to AAA again. For list initialization, this restriction of doing only one user defined conversion does not exist.

查看更多
登录 后发表回答