struct B {
B();
operator B*()
{ return (this); }
};
struct A {
A (const B *);
void operator= (B*);
};
void
mumble() {
B thing;
A a1 (thing); // ok, ok constructor
a1 = thing; // ok, assignment operator
A a2 = thing; // error, why??
}
--
Dave Brennan HaL Computer Systems
bre...@hal.com (512) 794-2855
>This example says it all:
>struct B {
> B();
> operator B*()
> { return (this); }
>};
>struct A {
> A (const B *);
> void operator= (B*);
>};
>void
>mumble() {
> B thing;
> A a1 (thing); // ok, ok constructor
> a1 = thing; // ok, assignment operator
> A a2 = thing; // error, why??
>}
A very similar example is depicted in ARM 12.6.1, pg 287; along with
the reasoning for why it is invalid. It turns out that:
A a2 = thing; is implicitly converted to A a2(thing). However this
requires yet another implicit conversion via B::operator B*. Two
implicit user defined conversions are not allowed, so the initializer
fails.
If this seems obscure to you, it seems obscure to me too. ;^)
--
Robert Martin | Design Consulting | Training courses offered:
Object Mentor Assoc.| rma...@rcmcon.com | Object Oriented Analysis
2080 Cranbrook Rd. | Tel: (708) 918-1004 | Object Oriented Design
Green Oaks IL 60048 | Fax: (708) 918-1023 | C++
|> bre...@hal.com (Dave Brennan) writes:
|> >This example says it all:
|> >struct B {
|> > B();
|> > operator B*()
|> > { return (this); }
|> >};
|> >struct A {
|> > A (const B *);
|> > void operator= (B*);
|> >};
|> >void
|> >mumble() {
|> > B thing;
|> > A a1 (thing); // ok, ok constructor
|> > a1 = thing; // ok, assignment operator
|> > A a2 = thing; // error, why??
|> >}
|> A very similar example is depicted in ARM 12.6.1, pg 287; along with
|> the reasoning for why it is invalid. It turns out that:
|> A a2 = thing; is implicitly converted to A a2(thing). However this
|> requires yet another implicit conversion via B::operator B*. Two
|> implicit user defined conversions are not allowed, so the initializer
|> fails.
If this were the case ('A a2 = thing' getting implicitly converted to
'A a2( thing )'), then it would work, exactly as it works in
'A a1( thing )'. In fact, there is a subtle difference in the
semantics of the two initializations; the first (without the '=')
calls the constructor for A directly, using 'thing' as a parameter.
The second (with the '=') uses the copy constructor to initialize
'a2'. There is one obvious difference; to use the form with '=', a
copy constructor must be present and accessible. (In this case, since
the programmer didn't say otherwise, the compiler has provided one.)
But there is a subtle difference in the type matching semantics, too.
In the initialization without the '=', the compiler searches *all* of
the constructors for the best match; in this case, it finds 'A( const B* )',
which is the only match, and requires *one* user conversion (using
operator B*() to convert B->B*). With the '=', the initialization
uses the copy constructor, period. To do this, it must convert
'thing' to an A. But, as pointed out, this requires *two* user
defined conversions, and is thus illegal.
Of course, once having decided that it has to use the copy
constructor, and having found a way to convert a B to an A, it may
optimize the copy constructor away. But it is only allowed to do so
once it has found the "correct" solution, *with* the copy constructor.
So the real difference is: without the '=', the compiler can choose
any constructor it wishes, and this constructor is *not* considered a
user defined conversion. With the '=', it must use the copy
constructor, and any other constructor needed to convert the parameter
to the target type *is* considered a user defined conversion.
|> If this seems obscure to you, it seems obscure to me too. ;^)
What is happening is nowhere near as obscure as why the language was
defined so that it happens this way. The ARM gives three effects (in
commentary); the above problem could be considered as part of the
first mentioned: "the set of values acceptable for initialization
[using the '=' initializer] and assignment will be identical except
where the user specifically defines constructors and/or assignment
operators to ensure they are not." And in fact, in the above, the
expression 'a2 = thing' would also be illegal if the user hadn't
defined an assignment operator. So the intention is, apparently, to
make initialization with '=' a sort of halfway house; the semantics
are those of initialization (constructor called), but the constraints
are vaguely similar to assignment, except when the user defines an
assignment operator, in which case, initialization with '=' has a set
of constraints all its own.
--
James Kanze email: ka...@us-es.sel.de
GABI Software, Sarl., 8 rue du Faisan, F-67000 Strasbourg, France
Conseils en informatique industrielle --
-- Beratung in industrieller Datenverarbeitung
|> |> If this seems obscure to you, it seems obscure to me too. ;^)
|> What is happening is nowhere near as obscure as why the language was
|> defined so that it happens this way. The ARM gives three effects (in
|> commentary); the above problem could be considered as part of the
|> first mentioned: "the set of values acceptable for initialization
|> [using the '=' initializer] and assignment will be identical except
|> where the user specifically defines constructors and/or assignment
|> operators to ensure they are not." And in fact, in the above, the
|> expression 'a2 = thing' would also be illegal if the user hadn't
|> defined an assignment operator. So the intention is, apparently, to
|> make initialization with '=' a sort of halfway house; the semantics
|> are those of initialization (constructor called), but the constraints
|> are vaguely similar to assignment, except when the user defines an
|> assignment operator, in which case, initialization with '=' has a set
|> of constraints all its own.
One thing does occur to me. Don't forget that the initialization of
function parameters and function return values has the semantics of
initialization *with* '='. So it will have the same constraints,
neither those of assignment, nor those of initialization using '()'.