Can someone explain how this function overload resolution is ambiguous?
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's 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's API can't be changed.
void other_function( User_Type< char > const& );
void other_function( User_Type< int > const& );
/// This is user-code. It's 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 receive 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 other_function( char ) to be other_function( User_Type< char > const& ), and the best match for other_function( int ) to be other_function( User_Type< int > const& ).
I understand a type conversion must take place for each argument to other_function. I'd expect char to User_Type< char > to be a better choice than char to User_Type< int >, which could be allowed by char to int promotion. I'd expect int to User_Type< int > to be a better choice than int to User_type< char >, which could be allowed by int to char conversions.
Additionally, if I create a local User_Type< T > user_var from a T, then I can unambiguously call other_function( user_var ). Semantically, this should be equivalent to the first, original statement.
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 ctor User_Type<char>( char const& ), which accepts an int, and User_Type<int> has ctor User_Type<int>( int const& ), which accepts a char.
Your conversions fall under the ranking of "User-Defined conversion sequence," and since both of those conversions are possible, both are included in the overload set with equal ranking. Hence the call is ambiguous. (See 13.3.3.2 Ranking implicit conversion sequences in the standard for more information.)
I think the problem here is trying to pick an overload based on qualifying 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 for conversions. In turn, though, char and int have implicit conversion available so it isn't clear which one you want.
If for this 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With