Reputation: 12259
#include <iostream>
struct int_wrapper
{
int i;
int_wrapper(int i = 0) : i(i) {}
};
struct A
{
int_wrapper i;
int_wrapper j;
A(int_wrapper i = 0, int_wrapper j = 0) : i(i), j(j) {}
};
int main()
{
A a;
a = {3};
// error: no match for ‘operator=’ (operand types are ‘A’ and ‘int’)
// a = 3;
std::cout << a.i.i << std::endl;
return 0;
}
I know that isn't allowed more than one user-conversion when doing an implicit conversion, but, why with a brace-init-list the double user-conversion works?
Upvotes: 6
Views: 3863
Reputation: 473407
Remember: using a braced-init-list means "initialize an object". What you are getting is an implicit conversion followed by an object initialization. You're getting "double user-conversion" because that's what you asked for.
When you do a = <something>;
, what this does is the effective equivalent of a.operator=(<something>)
.
When that something is a braced-init-list, this means that it will do a.operator=({3})
. That will pick an operator=
overload, based on initializing their first parameter from a braced-init-list. The overload that will be called will be the one that takes a type which can be initialized by the values in the braced-init-list.
There are two overloads of that operator. Namely, the copy-assignment and move-assignment. Since this braced-init-list initializes a prvalue, the preferred function to call will be the move-assignment operator (not that this matters, since it all leads to the same thing). The parameter of the move-assignment is A&&
, so the braced-init-list will attempt to initialize an A
. And it will do so via the rules of copy-list-initialization.
Having selected the operator=
function to call, we now initialize A
. Since the constructor of A
is not explicit
, copy-list-initialization is free to call it. Since that constructor has 2 default parameters, it can be called with only a single parameter. And the type of the first parameter in A
's constructor, int_wrapper
, is implicitly convertible from the type of the first value in the braced-init-list, int
.
So, you get an implicit conversion to int_wrapper
, which is used by copy-list-initialization to initialize a prvalue temporary object, which is used to assign to an existing object of type A
via move-assignment.
By contrast, a.operator=(3)
attempts to implicitly convert from 3 to A
directly. Which of course requires two conversion steps and therefore is not allowed.
Just remember that braced-init-lists mean "initialize an object".
Upvotes: 7