Conversion: via constructor or operator

M

Michael Lehn

Hi,

I have a question regarding the conversion of objects. When is the
conversion done by the constructor and when by the operator. My feeling
tells me that the constructor is preferred. But I couldn't find the
exact rule in the C++ standard.

And what if the classes have template parameters?

It would be great if somebody could get me a rough hint where in the
standard I should start reading.

I know that it is dangerous to have both a conversion constructor and a
conversion operator. But I want to learn more about this whole
mechanism.

Thanks,
Michael

//---------------------------------------
struct A;

struct B
{
B(const A &a);
};

struct A
{
operator B() const {}
};

B::B(const A &a) {}

int
main()
{
A a;
B b = a; // Uses always the constructor of B if possible?
return 0;
}
//----------------------------------------
 
A

Alf P. Steinbach

* Michael Lehn:
Hi,

I have a question regarding the conversion of objects. When is the
conversion done by the constructor and when by the operator. My feeling
tells me that the constructor is preferred.

If you mean, should you implement a conversion by providing a
constructor, or by providing an operator, then sometimes that is
clearcut, and sometimes it's personal preference.

If you mean, can one be called preferentially if both are defined, then
no, not in a conforming implementation.

A conforming compiler will reject such code.

But I couldn't find the exact rule in the C++ standard.

And what if the classes have template parameters?

It would be great if somebody could get me a rough hint where in the
standard I should start reading.

I know that it is dangerous to have both a conversion constructor and a
conversion operator. But I want to learn more about this whole
mechanism.

It's not dangerous: it's just not allowed.

//---------------------------------------
struct A;

struct B
{
B(const A &a);
};

struct A
{
operator B() const {}
};

B::B(const A &a) {}

int
main()
{
A a;
B b = a; // Uses always the constructor of B if possible?
return 0;
}
//----------------------------------------

MSVC 7.1 incorrectly compiles this. g++ 3.4.4 rejects it, correctly.
Comeau 4.3.3 (huh, what's with those numbers?) rejects it, correctly.
 
G

Greg

Alf said:
* Michael Lehn:

If you mean, should you implement a conversion by providing a
constructor, or by providing an operator, then sometimes that is
clearcut, and sometimes it's personal preference.

If you mean, can one be called preferentially if both are defined, then
no, not in a conforming implementation.

A conforming compiler will reject such code.



It's not dangerous: it's just not allowed.



MSVC 7.1 incorrectly compiles this. g++ 3.4.4 rejects it, correctly.
Comeau 4.3.3 (huh, what's with those numbers?) rejects it, correctly.

But gcc 4.0 does compile this program which suggests that gcc 3.4.4's
failure to compile this program was an error. And a close reading of
the Standard certainly provides no reason why there should be an error
in this case. There is only one user-defined conversion that could
match B b = a, and it is not the one selected by the overload
resolution as the best match anyway.

Since B's constructor accepts a const A& parameter, the variable, a,
used to initialize b in

B b = a;

is an "exact match" for the type needed to construct a B object
directly. No conversion takes place: a is passed to B's constructor, as
is. If that were not the case, and the initializer, a, had to be
converted to some other type in order to match the type of parameter
declared in B's constructor, then there would exist two user-defined
conversions that could be invoked to construct b. And if one of those
conversion sequences were to be selected as the best match then at that
point an ambiguous conversion error would be reported.

Greg
 
A

Alf P. Steinbach

* Greg:
But gcc 4.0 does compile this program which suggests that gcc 3.4.4's
failure to compile this program was an error. And a close reading of
the Standard certainly provides no reason why there should be an error
in this case.

You need to read even closer...

There is only one user-defined conversion that could
match B b = a, and it is not the one selected by the overload
resolution as the best match anyway.

Since B's constructor accepts a const A& parameter, the variable, a,
used to initialize b in

B b = a;

is an "exact match" for the type needed to construct a B object
directly.

The B object is not constructed directly except after optimization, and
that _possible_ optimization does not affect which rules are in play;
formally 'b' is here copy-constructed from a B object.

It would be different if the OP had used direct initialization (the C++
initialization syntax).

Then you'd have an exact match.

No conversion takes place: a is passed to B's constructor, as
is. If that were not the case, and the initializer, a, had to be
converted to some other type in order to match the type of parameter
declared in B's constructor, then there would exist two user-defined
conversions that could be invoked to construct b.

Yes, that's the situation here. 'a' must be converted to a B, which is
then passed to B's copy constructor. There are two ways to convert 'a'
to a B in a single step, hence the conversion is ambigious.

And if one of those
conversion sequences were to be selected as the best match then at that
point an ambiguous conversion error would be reported.

That's what a conforming compiler such as Comeau does.


PS: It would be nice if you could report the bug in g++ 4.0!
 
V

Valentin.Samko

Alf said:
That's what a conforming compiler such as Comeau does.

struct A;
struct B { B() {} B(const A &a) {} };
struct A { operator B() const { return B(); } };

int main()
{
A a;
B b = a;
}

Strange, Comeau 4.3.3 actually compiles this code fine. It does not
compile the original code, but this is only because the body of
"operator B() const" is empty, which is a completely different error.
 
A

Alf P. Steinbach

* Valentin.Samko:
struct A;
struct B { B() {} B(const A &a) {} };
struct A { operator B() const { return B(); } };

int main()
{
A a;
B b = a;
}

Strange, Comeau 4.3.3 actually compiles this code fine.

Sorry, you're wrong.

Comeau 4.3.3 does not compile your code, even in non-strict mode.

You can try it out at <url: http://www.comeaucomputing.com/tryitout/>.

Comeau C/C++ 4.3.3 (Aug 6 2003 15:13:37) for ONLINE_EVALUATION_BETA1
Copyright 1988-2003 Comeau Computing. All rights reserved.
MODE:non-strict warnings C++

"ComeauTest.c", line 8: error: more than one user-defined conversion
from "A" to "B"
applies:
function "A::eek:perator B() const"
function "B::B(const A &)"
B b = a;
^

1 error detected in the compilation of "ComeauTest.c".

It does not
compile the original code, but this is only because the body of
"operator B() const" is empty, which is a completely different error.

Sorry, you're wrong.
 
V

Valentin Samko

Alf said:
Sorry, you're wrong.
It compiles this code fine using vc++7.1 as a backend, without --strict.
Why would I write this if it didn't compile the code for me?
Comeau 4.3.3 does not compile your code, even in non-strict mode.
It does, using vc7.1 as a backend.
I just used my copy.
>como --vc71 0.cpp
como: Warning: COMO_MS_INCLUDE environment variable missing
Comeau C/C++ 4.3.3 (Jan 13 2004 11:29:09) for MS_WINDOWS_x86
Copyright 1988-2003 Comeau Computing. All rights reserved.
MODE:non-strict warnings microsoft C++
>aout
>
Sorry, you're wrong.
Again, this depends on the backend compiler and options. I am quite right according to my
copy of comeau, using default options.
 
A

Alf P. Steinbach

* Valentin Samko:
It compiles this code fine using vc++7.1 as a backend, without --strict.
Why would I write this if it didn't compile the code for me?

It does, using vc7.1 as a backend.

Grumble.

Anyway, the code's formally incorrect.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,764
Messages
2,569,567
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top