The question rose in context of this answer.
Consider an example:
struct foo {
int value;
operator int&(){ return value; }
operator int(){ return value; }
};
int main () {
int &a(foo{}); // #1
//int &b{foo{}}; // #2 -- ambiguity
int &c = foo{}; // #3
//int &d = {foo{}}; // #4-- ambiguity
int &d { a }; // #5
int &e = { a }; // #6
(void)a;
(void)c;
(void)d;
(void)e;
}
I don't understand why does #2 and #4 cause ambiguity while #1 and #3 does not. So the question is - why does direct list initialization causes ambiguity for implicit cast to reference if cast operators to the type and reference to the type are declared?
List initialization, when used to initialize a reference, will take the listed values and convert them into a prvalue (aka: a temporary), which will be used to direct initialize the reference.
So int &b{foo{}}
is functionally equivalent to int &b(int(foo{}))
. Which is ambiguous; it could generate that int
via operator int
or operator int&
.
But even if it wasn't ambiguous, you would still be getting a non-const lvalue reference to a prvalue. Which is illegal. So this code was never going to work.
Braced-init-lists (curly braces) initialize objects, not references to objects. If you already have an object and want to get a reference to it, don't use braced-init-lists.
But in this case why does compiler accept #5?
Because list initialization is a series of rules with priority. A rule that has a higher priority than the one I pointed out above is the case of a braced-init-list which contains a single value, who's type is identical to the type of what is being initialized. #5 and 6 just so happen to fit that bill, since d
, e
, and a
are all int&
s.
But if you just take my advice and not use braced-init-lists when you're not trying to create an object, you won't have to worry about corner-cases like that.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With