Template conversion operator ambiguity?

I

Ian Collins

While "porting" some existing code to g++, I ran into a problem with a
generic value class that uses a template conversion operator to return
values. Here's a stripped down version:

#include <string>

struct Value
{
Value() {}

template <typename T> operator T() { return T(); }
};

int main()
{
Value v;

int n(v);
std::string s(v);
}

My original compiler is happy with the string initialisation, but g++
whinges about the conversion being ambiguous (edited to cut the noise):

example.cc:15: error: call of overloaded 'basic_string(Value&)' is ambiguous
candidates are:
std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*,
const _Alloc&)

std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const
std::basic_string<_CharT, _Traits, _Alloc>&)

std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _Alloc&)

But it will happily accept

std::string s = v;

So my question is, which compiler is correct?
 
A

Andrey Tarasevich

Ian said:
#include <string>

struct Value
{
Value() {}

template <typename T> operator T() { return T(); }
};

int main()
{
Value v;

int n(v);
std::string s(v);
}

My original compiler is happy with the string initialisation, but g++
whinges about the conversion being ambiguous (edited to cut the noise):

example.cc:15: error: call of overloaded 'basic_string(Value&)' is ambiguous
candidates are:
std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*,
const _Alloc&)

std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const
std::basic_string<_CharT, _Traits, _Alloc>&)

std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _Alloc&)

But it will happily accept

std::string s = v;

So my question is, which compiler is correct?

G++ is correct for rather obvious reasons.

Your first `std::string` initialization is ambiguous. As the compiler
told you, in the

std::string s(v);

any of the three listed constructors can be used. None of then is
"better" than the others from the overload resolution point of view.
They all are equally good, hence the ambiguity.

The

std::string s = v;

variant also technically allows several "paths" the lead to creation of
the temporary `std::string` object on the right-hand side, but one of
them is considered "better" then the others: it is "better" create a
temporary `std::string` immediately from `v` than create, say, a 'char
*` pointer from 'v' and then convert it to temporary `std::string`. In
other words, there's a clear "winner" for the overload resolution in
this case and, therefore, no ambiguity.
 
I

Ian Collins

G++ is correct for rather obvious reasons.

Your first `std::string` initialization is ambiguous. As the compiler
told you, in the

std::string s(v);

any of the three listed constructors can be used. None of then is
"better" than the others from the overload resolution point of view.
They all are equally good, hence the ambiguity.

The

std::string s = v;

variant also technically allows several "paths" the lead to creation of
the temporary `std::string` object on the right-hand side, but one of
them is considered "better" then the others: it is "better" create a
temporary `std::string` immediately from `v` than create, say, a 'char
*` pointer from 'v' and then convert it to temporary `std::string`. In
other words, there's a clear "winner" for the overload resolution in
this case and, therefore, no ambiguity.

That's clear, thanks. That was one of those cases where the incorrect
behaviour was preferable!
 
J

James Kanze

While "porting" some existing code to g++, I ran into
a problem with a generic value class that uses a template
conversion operator to return values. Here's a stripped down
version:
#include <string>
struct Value
{
Value() {}
template <typename T> operator T() { return T(); }
};
int main()
{
Value v;

int n(v);
std::string s(v);
}
My original compiler is happy with the string initialisation,
but g++ whinges about the conversion being ambiguous (edited
to cut the noise):
example.cc:15: error: call of overloaded 'basic_string(Value&)' is ambiguous
candidates are:
std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*,
const _Alloc&)
std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const
std::basic_string<_CharT, _Traits, _Alloc>&)
std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _Alloc&)
But it will happily accept

std::string s = v;
So my question is, which compiler is correct?

I'd say g++. In the original code, the compiler is supposed to
convert v to something that can be used to call a constructor of
std::string: since there are several constructors which take
a single argument, the compiler can't choose between them, and
the call is ambiguous.

When you modify the definition to use copy initialization (with
the = sign), the semantics are: convert the expression to
std::string, then use the copy constructor. In this case,
there's only one conversion the std::string which doesn't
involve more than one user defined conversions, so there is no
ambiguity.
 

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,955
Messages
2,570,117
Members
46,705
Latest member
v_darius

Latest Threads

Top