Search code examples
c++implicit-conversionoverload-resolution

Overload resolution works on functions, but not with implicit constructor parameters


This code shows a basic wrapper class for the __int32 and bool type. My intention is to extend basic types with methods and custom operators, etc.

Each class has one implicit constructor, allowing to assign an __int32 to an Int32 type implicitly. This works well, until there is a function overload accepting both Int32 or Boolean.

While a function with the overloads __int32 and bool compiles and works, the function with the classes as overloads results in Ambiguous calls to overloaded function.

My question: Is there a way to solve this properly, so I can implement wrapper classes for basic types without overload resolution problems and without the need for explicit casts by the caller?

class Boolean
{
private:
    bool _Value = false;
public:
    Boolean(bool value)
    {
        _Value = value;
    }
};

class Int32
{
private:
    __int32 _Value = 0;
public:
    Int32(__int32 value)
    {
        _Value = value;
    }
};

void AmbiguousFunc(const Int32 &x) { }
void AmbiguousFunc(const Boolean &x) { }

void NotAmbiguous(__int32 x) { }
void NotAmbiguous(bool x) { }

int main(int argc, char *argv[])
{
    AmbiguousFunc(123);         // Ambiguous calls to overloaded function
    AmbiguousFunc((Int32)123);  // works
    NotAmbiguous(123);

    return 0;
}

Solution

  • The problem is, given AmbiguousFunc(123);, 123 could be converted to bool (standard conversion), then converted to Boolean(user-defined conversion); which has the same ranking with the user-defined conversion from 123 to Int32, then the calling is ambiguous between AmbiguousFunc(const Int32 &) and AmbiguousFunc(const Boolean &).

    You can change the constructors as template, and restrict them to accept only appropriate types.

    class Boolean
    {
    private:
        bool _Value = false;
    public:
        // accept bools only; no __int32s
        template <typename T, std::enable_if_t<std::is_same_v<T, bool>>* = nullptr>
        Boolean(T value)
        {
            _Value = value;
        }
    };
    
    class Int32
    {
    private:
        __int32 _Value = 0;
    public:
        // accept __int32s only; no bools
        template <typename T, std::enable_if_t<std::is_same_v<T, __int32>>* = nullptr>
        Int32(T value)
        {
            _Value = value;
        }
    };
    

    LIVE