Search code examples
c++constructorambiguity

Ambiguous calls to class constructors


I am currently implementing a custom string class, but I am facing some issues with the class constructors. The class is a custom wrapper for std::string, so the class have most of the std::string constructors, except for the iterators constructors since some systems where the code will run does not support STL.

The class also provides some constructors based on Arduino String implementation, specifically the numeric variables to string constructors, which are shown below:

Number Conversion Constructors:

// unsigned int
    string(const uint8_t Source, uint8_t Base =  10, bool LetterCase = CPString::NumberConversion::LetterCase::Mode);
    string(const uint16_t Source, uint8_t Base = 10, bool LetterCase = CPString::NumberConversion::LetterCase::Mode);
    string(const uint32_t Source, uint8_t Base = 10, bool LetterCase = CPString::NumberConversion::LetterCase::Mode);
    string(const uint64_t Source, uint8_t Base = 10, bool LetterCase = CPString::NumberConversion::LetterCase::Mode);

// signed int
    string(const int8_t Source, uint8_t Base = 10, bool LetterCase = CPString::NumberConversion::LetterCase::Mode, bool IntFormat = CPString::NumberConversion::IntFormat::Mode);
    string(const int16_t Source, uint8_t Base = 10, bool LetterCase = CPString::NumberConversion::LetterCase::Mode, bool IntFormat = CPString::NumberConversion::IntFormat::Mode);
    string(const int32_t Source, uint8_t Base = 10, bool LetterCase = CPString::NumberConversion::LetterCase::Mode, bool IntFormat = CPString::NumberConversion::IntFormat::Mode);
    string(const int64_t Source, uint8_t Base = 10, bool LetterCase = CPString::NumberConversion::LetterCase::Mode, bool IntFormat = CPString::NumberConversion::IntFormat::Mode);

// float
    explicit string(const float Source);
    string(const float Source, uint8_t precision);

As a simple brief: Base represents the number base to be used to convert the variable, where 2 would be binary, 8 octal, and 16 hex, the usable number bases are [2,32]. And LetterCase is a variable that defines if upper case or lower case lettering should be used.

The problem is that this set of contructors become ambiguous with one of the std::string contructors, specifically the fill constructor, (below the definition):

string(unsigned int n, char c);

When calling:

CPString::string myString(10,'a');

The compiler throws error C2666: overloaded functions have similar conversion

I assume that the char literal is being cast to uint8_t, is it possible to unambiguiate this constructors? I know that changing 10 to 10u unambiguates the call, but I want to find a better solution. That would also accept 10 as a first parameter for the fill constructor.


Solution

  • The problem you're facing is that the fixed width integer types are aliases, not separate types. Often, uint8_t is just another name for unsigned char. A different name, not a different type.

    One solution that also increases readability is to introduce a scoped enumeration. For example:

    enum class Base : uint8_t {
        Base2 = 2,
        Base3,
        Base4,
        Base5,
        Base6,
        // ...
        // You get the idea, I hope
        // ...
        Base31,
        Base32
    };
    

    This Base type is distinct from all other types, yet it's the same size as uint8_t. Using this as the second parameter to the "Number Conversion Constructors" would keep the signatures basically the same, but they would no longer be ambiguous, and the call sites would be easier to read (Base16 says that it is a base more clearly than 16).