Take a simple class wrapping an int
,
struct Foo {
int x;
} f;
and a class that holds a Foo
and that can be converted to it,
struct Bar {
Foo f;
operator Foo&() {
return f;
}
operator Foo const&() const {
return f;
}
Bar& operator=(Bar const&) = default;
} b;
What part of the standard, if any, makes this invalid
b = f;
instead of being equivalent to this?
static_cast<Foo&>(b) = f;
(I'm not saying it should, nor that it would be normal, expected or anything like that.)
Here I read that
For the built-in assignment operators, conversions of the left operand are restricted as follows:
- [...]
- no user-defined conversions are applied to the left operand to achieve a type match with the left-most parameter of a built-in candidate.
For all other operators, no such restrictions apply.
So am I misunderstanding the meaning of "built-in assignment operator" and the =
in b = f
is the built-in =
to which that restriction applies?
Or is that =
not the bult-in and so that restriction does not apply but the code is flawed for other reasons?
Paragraph 3 there describes the candidate set:
- For a unary operator @ with an operand of type cv1 T1, and for a binary operator @ with a left operand of type cv1 T1 and a right operand of type cv2 T2, four sets of candidate functions, designated member candidates, non-member candidates, built-in candidates, and rewritten candidates, are constructed as follows:
- If
T1
is a complete class type or a class currently being defined, the set of member candidates is the result of a search foroperator@
in the scope ofT1
; otherwise, the set of member candidates is empty.- For the operators
=
,[]
, or->
, the set of non-member candidates is empty; otherwise [...]
So for b = f
, our member candidates are b.operator=(f)
and we have no non-member candidates.
The paragraph that you cited talks about when the built-in candidates are viable, but built-in are those provided by the language (in [over.built]) - those could apply if your type is convertible to something like int&
. Those aren't relevant here.
But in this case, b
is convertible to Foo&
, and there's no mechanism here to consider Foo::operator=
as a candidate. We only have the member candidates to consider, and only our own (Bar
's) members - not arbitrary other types' members.