Search code examples
c++overloadingoverload-resolution

Understanding method overloading rules


I have a string class:

#include <stdint.h>

class CString {
public:
    CString() {}
    explicit CString(uint64_t ui64, int width = -1, int base = 10, char fillChar = ' ') {}
    CString(const char * str) {}

    CString arg(const CString& argument) const {return CString();}

    template <typename T>
    inline CString arg(T argument, int width = -1, int base = 10, char fillChar = ' ') const
    {
        return arg(CString(argument, width, base, fillChar));
    }
};

Now, I'm using this class:

void main()
{
    CString str;
    str.arg("123");
}

And get the error:

error C2665: 'CString::CString' : none of the 4 overloads could convert all the argument types
1>          source.cpp(6): could be 'CString::CString(uint64_t,int,int,char)'
1>          while trying to match the argument list '(const char *, int, int, char)'
1>          source.cpp(22) : see reference to function template instantiation 'CString CString::arg<const char*>(T,int,int,char) const' being compiled
1>          with
1>          [
1>              T=const char *
1>          ]

Why doesn't arg(CString&) get called, using the CString(const char *) constructor?


Solution

  • When you call

    str.arg("123");
    

    the best match is obtained via the function template arg, since CString arg(const CString& argument) requires a conversion to a user defined type. The function template then calls

    return arg(CString(argument, width, base, fillChar));
    

    but there is no CString constructor that matches the argument list const char*, int, int, char that you are passing here.

    There can be no call to CString(uint64_t, int, int, char) because the conversion from const char* to uint64_t is an invalid conversion. This simplified code sample exhibits the same problem:

    #include <cstdint>
    void foo(std::uint64_t) {}
    
    int main()
    {
      foo("hello");
    }
    

    error: invalid conversion from 'const char*' to 'uint64_t {aka long long unsigned int}' [-fpermissive]

    foo("hello");

    If you were to comment out the member function template arg, then CString arg(const CString& argument) would be called, via the implicit conversion from const char* to CString.