Consider the following code:
struct A {
int x;
};
int main() {
A a;
A b{a};
}
Is this program well-formed at C++11 standard? In my copy of N3797 it says
8.5.4 List initialization
[dcl.init.list]3: List-initialization of an object or reference of type
Tis defined as follows:
- IfTis an aggregate, aggregate initialization is performed (8.5.1).
- Otherwise, ifTis a specialization ofstd::initializer_list<E>, ...
- Otherwise, ifTis a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen using overload resolution. If a narrowing conversion is required to convert any of the types, the program is ill-formed.
- Otherwise, if the initializer list has a single element of typeEand eitherTis not a reference type or it is reference-related toE, the object or reference is initialized from that element; if a narrowing conversion is required to convert the element toT, the program is ill-formed.
- Otherwise, ifTis a reference type, a pr-value temporary of the type reference byTis copy-list-initialized or direct-list-initialized, depending on the kind of initialization for the reference, and the reference is bound to that temporary.
- Otherwise, if the initializer list has no elements, the object is value-initialized.
- Otherwise, the program is ill-formed.
The point of the example is, the type is an aggregate, but list-initialization is supposed to invoke the copy constructor. On gcc 4.8 and gcc 4.9, at C++11 standard, it fails:
main.cpp: In function ‘int main()’:
main.cpp:7:8: error: cannot convert ‘A’ to ‘int’ in initialization
A b{a};
^
and says A is not convertible to int or similar, because aggregate initialization fails. On gcc 5.4, it works fine at C++11 standard.
On clang you get similar errors with clang-3.5, 3.6, and it starts working at clang-3.7.
I understand that it is well-formed at C++14 standard, and that it was mentioned in a defect report here.
However, what I don't understand is why this was considered a defect in the standard.
When the standard writes,
"If X, foo-initialization is performed. Otherwise, if Y, bar-initialization is performed, .... Otherwise, the program is ill-formed.",
doesn't this mean that if X holds, but foo-initialization cannot be performed, then we should check if Y holds, and then attempt bar-initialization?
This would make the example work, because when aggregate initialization fails, we don't match std::initializer_list, and the next condition we match is "T is a class type", and then we consider constructors.
Note that this does seem to be how it works in this modified example
struct A {
int x;
};
int main() {
A a;
const A & ref;
A b{ref};
}
All the same compilers treat this the same way as the earlier example, at C++11 and C++14 standards. But it seems that the modified wording from the CWG defect record doesn't apply to this case. It reads:
If
Tis a class type and the initializer list has a single element of typecv Tor a class type derived fromT, the object is initialized from that element.
But in the second code example, the initializer list technically contains const T &. So I don't see how it would work unless after aggregate initialization fails, we are supposed to attempt constructors.
Am I wrong? Is it not supposed to attempt constructors after aggregate initialization fails?
Here's a related example:
#include <iostream>
struct B {
int x;
operator int() const { return 2; }
};
int main() {
B b{1};
B c{b};
std::cout << c.x << std::endl;
}
In clang-3.6, gcc-4.8, gcc-4.9, it prints 2, and in clang-3.7, gcc-5.0 it prints 1.
Assuming I'm wrong, and at C++11 standard, list initialization of an aggregate is supposed to be aggregate initialization and nothing else, until the new wording in the defect report is introduced, is it a bug that this happens even when I select -std=c++11 on the newer compilers?
Aucun commentaire:
Enregistrer un commentaire