Why is this function overloaded with type conversion of arguments ambiguous?
Can someone explain how is this invalid function overload resolution?
Given:
/// This type *contains* a @c T.
template< typename T >
class User_Type
{
public:
/// This <em>conversion constructor</em> is a key part of it API;
/// it won't likely change.
User_Type( T const & ar_data )
: m_data( ar_data )
{}
private:
T m_data;
};
/// @c some_value is just a templated function that generates a @c T.
template< typename T > T some_value();
template<> char some_value();
template<> int some_value();
/// This overloaded, non-templated function represents some third-party
/// code somewhere else; it API can't be changed.
void other_function( User_Type< char > const& );
void other_function( User_Type< int > const& );
/// This is user-code. It contents exercise some aspect of the 'User_Type' API.
/// This code can change.
template< typename T >
void function()
{
other_function( some_value< T >() ); /* AMBIGUOUS CALL */
User_Type< T > user_var = some_value< T >(); /* UNAMBIGUOUS CONVERSION */
other_function( user_var ); /* UNAMBIGUOUS CALL */
}
template void function< char >();
template void function< int >();
and compiling with g++-4.9 -Wall -Wextra
, I get the following errors:
In instantiation of โvoid function() [with T = char]โ:
error: call of overloaded โother_function(char)โ is ambiguous
note: candidates are:
note: void other_function(const User_Type<char>&)
note: void other_function(const User_Type<int>&)
In instantiation of โvoid function() [with T = int]โ:
error: call of overloaded โother_function(int)โ is ambiguous
note: candidates are:
note: void other_function(const User_Type<char>&)
note: void other_function(const User_Type<int>&)
I expect the best match for to other_function( char )
be other_function( User_Type< char > const& )
and the best match for other_function( int )
is other_function( User_Type< int > const& )
.
I understand that a type conversion must take place for every argument other_function
. I would have expected char
before to User_Type< char >
be a better choice than char
before User_Type< int >
, which might be allowed by promoting char
before int
. I would expect that int
to User_Type< int >
be a better choice than int
before User_Type< char >
, which may be permitted transformations int
to char
.
Also, if I create a local User_Type< T > user_var
from T
then I can unambiguously call other_function( user_var )
. Semantically, this should be equivalent to the first, original statement.
source to share
Since there is no exact match for other_function(char)
or other_function(int)
,
other_function( some_value< T >() );
must implicitly convert its argument to match one of:
void other_function( User_Type< char > const& );
void other_function( User_Type< int > const& );
but User_Type<char>
has a ctor User_Type<char>( char const& )
that accepts int
and User_Type<int>
has a ctor User_Type<int>( int const& )
that accepts a char
.
Your conversions are ranked as "User-Defined Conversion Sequence", and because both conversions are possible, both are included in the equal rank overload set. Hence the challenge is ambiguous. (See 13.3.3.2. Introducing implicit conversion sequences into the standard for more information.)
source to share
I think the problem here is trying to pick an overload based on qualification conversions. I can take out some of the templates and get the same result:
class User_Type_char
{
public:
User_Type_char(char const &) {}
};
class User_Type_int
{
public:
User_Type_int(int const &) {}
};
void other_function( User_Type_char const& );
void other_function( User_Type_int const& );
template< typename T >
void function()
{
other_function( 'a' ); /* AMBIGUOUS CALL */
}
The problem is that there is no perfect match in the overload list, so we start checking conversions. In turn, however, char
and int
have an implicit conversion, so it is not clear which one you want.
If for the same code I change the constructor of the first class
class User_Type_char
{
public:
User_Type_char(const char*) {}
};
Now the same call becomes unambiguous and calls the User_Type_int version.
source to share