bytecode77
bytecode77

Reputation: 14840

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;
}

Upvotes: 1

Views: 96

Answers (1)

songyuanyao
songyuanyao

Reputation: 172924

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

Upvotes: 3

Related Questions