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.

+3


source to share


2 answers


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.)

+2


source


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.

+1


source







All Articles