2
votes

I have a function template (c++)

template<typename T>
void print_to_default_file(T &obj, ADDON addon = "")

and a overloaded function

template<typename T>
void print_to_default_file(T &obj, std::string  objS) // or char* objS

The class ADDON has an operator overload of the following signature

void operator=(const std::string)

The problem is that when I do print_to_default_file("test","where will I go")

It is calling the first one, but I want to call the second one. I have tired also with char * instead of std::string but the result is same.

Could anyone please point out what is wrong

ADDON simplified version

class ADDON {
    std::string s;

public:
    ADDON() {
        s = "";
    }

ADDON(std::string in) {
    s = in;
}

    ADDON(const char in[]) {
        s = in;
    }

    void operator=(const std::string in) {
        s = in;
    }

    std::string getString() {
        return s;
    }
};
1
I doubt this very much. Both versions require user-defined conversion, but the second requires just one, so should be the one that's called.Lightness Races in Orbit
What compiler are you using? GCC considers the call to be ambiguous, see hereAndy Prowl
@AndyProwl I am using llvm 3.1simpleuser
@AndyProwl in g++ the char* version is working properlysimpleuser
something else: deafault arguments are always passed, in your case the empty string literal by-value. this is slight performance bug (compiler might optimize it away if you don't use it). defining the default string outside the function and letting the default parameter to const reference would eliminate it.TemplateRex

1 Answers

5
votes

Your original code does not compile

The code that you showed us

#include <iostream>
#include <string>

class ADDON {
    std::string s;

public:
    ADDON() {
        s = "";
    }

    ADDON(std::string in) {
        s = in;
    }

    ADDON(const char in[]) {
        s = in;
    }

    void operator=(const std::string in) {
        s = in;
    }

    std::string getString() {
        return s;
    }
};

template<typename T>
void print_to_default_file(T &obj, ADDON addon = "")
{ std::cout << "first overload\n"; }

template<typename T>
void print_to_default_file(T &obj, std::string  objS) 
{ std::cout << "second overload\n"; }

int main()
{
     print_to_default_file("test","where will I go");
}

does not compile (online output) with the following error

prog.cpp: In function ‘int main()’: prog.cpp:39:52: error: call of overloaded ‘print_to_default_file(const char [5], const char [16])’ is ambiguous prog.cpp:39:52: note: candidates are: prog.cpp:30:6: note: void print_to_default_file(T&, ADDON) [with T = const char [5]] prog.cpp:34:6: note: void print_to_default_file(T&, std::string) [with T = const char [5]; std::string = std::basic_string]

The reason is that name lookup and argument deduction find 2 candidates: the first overload requires a const char* to ADDON conversion, and the second overload a const char* to std::string conversion. Both conversion sequences are equally good matches and overload resoution is ambiguous and your program ill-formed.

A simple fix

Simply change the second overload to take a const char* as parameter (and not as char*, which cannot bind against string literals) and it will be the best match for raw string literals as argument

template<typename T>
void print_to_default_file(T &obj, const char*  objS) // but NOT char* objS
{ std::cout << "second overload\n"; }

You will now get the second overload (online output). To select the first overload, simply call the ADDON constructor in the parameter list

int main()
{
     print_to_default_file("test", ADDON("where will I go"));
}

Note that this will call the ADDON(const char[]) constructor and not the ADDON(std::string) one, since the latter will require a user-defined conversion (online output).

The proper fix

It is extremely dangerous to have non-explicit single argument constructors. Always use explicit keyword around such functions.

class ADDON {
    std::string s;

public:
    ADDON() {
        s = "";
    }

    explicit ADDON(std::string in) {
        s = in;
    }

    explicit ADDON(const char in[]) {
        s = in;
    }

    void operator=(const std::string in) {
        s = in;
    }

    std::string getString() {
        return s;
    }
};

This will also call the second overload (online output), because the ADDON overload does not explicitly call any constructor. To select the first overload, again call the ADDON constructor in the parameter list.