Ambiguous constructor call

X

xtrigger303

Hi to all,
I'm working on a smart pointer implementation and I'm trying to get
automatic type conversion between different pointer types. I stumbled
upon something weird (at least for me) I summarized it in the code
below. I was expecting both things at the end to work or not work at
all....
Any insight?
Thanks in advance,
Francesco


#include <iostream>

class A;

//

class B
{
public:

B() { std::cout << "B()\n"; }

B( B const & ) { std::cout << "B( B const & )\n"; }

~B() { std::cout << "~B()\n"; }

B & operator=( B const & ) { std::cout << "B & operator=( B const & )
\n"; return *this; }

template< typename T >
operator T() const;
};

//

class A
{
public:

A() { std::cout << "A()\n"; }

explicit A( int ) { std::cout << "A( int )\n"; }

A( A const & ) { std::cout << "A( A const & )\n"; }

~A() { std::cout << "~A()\n"; }

A & operator=( A const & ) { std::cout << "A & operator=( A const & )
\n"; return *this; }
};

//

template< typename T >
B::eek:perator T() const { std::cout << "B::eek:perator T() const\n";
return T(); }

//

int main( )
{
B obj001;
A obj002 = obj001; // this works
//A obj003( obj001 ); // this is ambiguous
}
 
?

=?ISO-8859-1?Q?Erik_Wikstr=F6m?=

Hi to all,
I'm working on a smart pointer implementation and I'm trying to get
automatic type conversion between different pointer types. I stumbled
upon something weird (at least for me) I summarized it in the code
below. I was expecting both things at the end to work or not work at
all....
Any insight?
Thanks in advance,
Francesco


#include <iostream>

class A;

//

class B
{
public:

B() { std::cout << "B()\n"; }

B( B const & ) { std::cout << "B( B const & )\n"; }

~B() { std::cout << "~B()\n"; }

B & operator=( B const & ) { std::cout << "B & operator=( B const & )
\n"; return *this; }

template< typename T >
operator T() const;
};

//

class A
{
public:

A() { std::cout << "A()\n"; }

explicit A( int ) { std::cout << "A( int )\n"; }

A( A const & ) { std::cout << "A( A const & )\n"; }

~A() { std::cout << "~A()\n"; }

A & operator=( A const & ) { std::cout << "A & operator=( A const & )
\n"; return *this; }
};

//

template< typename T >
B::eek:perator T() const { std::cout << "B::eek:perator T() const\n";
return T(); }

//

int main( )
{
B obj001;
A obj002 = obj001; // this works
//A obj003( obj001 ); // this is ambiguous
}

Since B can be converted to any type you like, it can be converted to
either A or int. Both of which are types for which A has a constructor,
and the compiler have no idea which of them you would like to use. Using
explicit on a constructor only prevents it from being used like this:

A a = 1;

You have to do

A a(1);
 
X

xtrigger303

Since B can be converted to any type you like, it can be converted to
either A or int. Both of which are types for which A has a constructor,
and the compiler have no idea which of them you would like to use. Using
explicit on a constructor only prevents it from being used like this:

A a = 1;

You have to do

A a(1);

In fact I was wondering why the first initialization works ( it is not
ambiguous it seems...)

while the second doesn't.

I figured that the explicit doesn't help here.
:)
 
M

Markus Schoder

Hi to all,
I'm working on a smart pointer implementation and I'm trying to get
automatic type conversion between different pointer types. I stumbled
upon something weird (at least for me) I summarized it in the code
below. I was expecting both things at the end to work or not work at
all....
Any insight?
Thanks in advance,
Francesco


#include <iostream>

class A;

//

class B
{
public:

B() { std::cout << "B()\n"; }

B( B const & ) { std::cout << "B( B const & )\n"; }

~B() { std::cout << "~B()\n"; }

B & operator=( B const & ) { std::cout << "B & operator=( B const & )
\n"; return *this; }

template< typename T >
operator T() const;
};

//

class A
{
public:

A() { std::cout << "A()\n"; }

explicit A( int ) { std::cout << "A( int )\n"; }

A( A const & ) { std::cout << "A( A const & )\n"; }

~A() { std::cout << "~A()\n"; }

A & operator=( A const & ) { std::cout << "A & operator=( A const & )
\n"; return *this; }
};

//

template< typename T >
B::eek:perator T() const { std::cout << "B::eek:perator T() const\n"; return
T(); }

//

int main( )
{
B obj001;
A obj002 = obj001; // this works

The standard mandates that this behaves as if a temporary A object is
created first from obj001 and then obj002 is copy constructed from this
temporary object (to further complicate things the actual copy
construction may be elided but the compiler must check that it would have
been possible). Creating the temporary object is an implicit conversion
and hence the explicit A(int) is not considered.
//A obj003( obj001 ); // this is ambiguous

This is an explicit constructor call hence A(int) as well as the copy
constructor are considered.
 
X

xtrigger303

The standard mandates that this behaves as if a temporary A object is
created first from obj001 and then obj002 is copy constructed from this
temporary object (to further complicate things the actual copy
construction may be elided but the compiler must check that it would have
been possible). Creating the temporary object is an implicit conversion
and hence the explicit A(int) is not considered.


This is an explicit constructor call hence A(int) as well as the copy
constructor are considered.

Thanks, I think I got it.
But what if I remove the explicit on the constructor.
Does the compiler choose the "shortest" implicit conversion?
This means it chooses:

1. B::eek:perator T() const [ with T=A ]

instead of

1. B::eek:perator T() const [ with T = int ]
and then
2. A( int ) // if I remove the explicit this could be used for
implicit conversion

It makes sense to choose the "shortest" path... I'm asking just to
make sure I got it.
Thanks again and sorry for the newbie questions
FB
 
F

Frank Birbacher

This means it chooses:

1. B::eek:perator T() const [ with T=A ]

instead of

1. B::eek:perator T() const [ with T = int ]
and then
2. A( int ) // if I remove the explicit this could be used for
implicit conversion

How is the first onw shorter than the second? It is still ambiguous:

first case:
B::eek:perator A ()
A(A const&)

second case:
B::eek:perator int()
A(int)

Just because in the first case the intermediate value is already of type
"A" does not make the conversion shorter. A reasonable approach would be
to provide a A(B const&) constructor.


Frank
 
?

=?ISO-8859-1?Q?Erik_Wikstr=F6m?=

Thanks, I think I got it.
But what if I remove the explicit on the constructor.
Does the compiler choose the "shortest" implicit conversion?

No, when writing 'A obj002 = obj001;' it means that the copy constructor
will be used (even though it might be elided), so the other constructors
do not matter.
 
X

xtrigger303

This means it chooses:
1. B::eek:perator T() const [ with T=A ]
instead of
1. B::eek:perator T() const [ with T = int ]
and then
2. A( int ) // if I remove the explicit this could be used for
implicit conversion

How is the first onw shorter than the second? It is still ambiguous:

first case:
B::eek:perator A ()
A(A const&)

second case:
B::eek:perator int()
A(int)

Just because in the first case the intermediate value is already of type
"A" does not make the conversion shorter. A reasonable approach would be
to provide a A(B const&) constructor.

Frank

Just to clarify: I'm working on a smart pointer library with different
templated smart pointers types ( like strong, weak, shared ) and a
central memory tracking system for debugging purposes. The example
above was just to show the problem... I'm actually working around the
problem but I was curious... Anyway the previously mentioned
simplified example is NOT ambiguous.

In fact


B obj1;
A obj2 = obj1;

compiles perfectly, this means that the compiler chooses one of the
two paths.
So either

1. B::eek:perator T() [ with T = A ]
2. A( A const & ) // eliding copy constructor

or

1. B::eek:perator T() [ with T = int ]
2. A( int ) // taking away explicit

Since the compiler chooses, I guess it chooses the first...
Thanks for the insights
FB
 
T

terminator

In fact I was wondering why the first initialization works ( it is not
ambiguous it seems...)

while the second doesn't.

I figured that the explicit doesn't help here.
:)- Hide quoted text -

- Show quoted text -

explicit causes the first not to be ambiguous.do:
template<typename T>
T& B::To();

A obj003(obj001.To<A>());

regards,
FM.
 

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

No members online now.

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,042
Latest member
icassiem

Latest Threads

Top