Frage

Editted - please skip to the edit, which has the real problem

I frequently run into situations in my string helper library of stand-alone functions, where I provide overloads of a function with versions that take a char and versions that take a std::string.

The problem is, the overload then becomes ambiguous when passed a string literal (const char*).

Example:

void myFunc(const std::string &subStr);
void myFunc(char character);

These two functions are implemented differently, one optimized for strings and one for a single char. Howsoever, trying to call myFunc("literal") results in ambiguity, despite me always wanting it to call the std::string version.

This forces me to provide void myFunc(const char *str) versions of my overloads, which only are stubs like:

void myFunc(const char *str)
{
    myFunc(std::string(str));
}

Is there some way to make these stub functions unnecessary? I'd like to just be able to make void myFunc(char c) 'explicit', but you can't make non-constructor non-member functions explicit. Which would solve the problem instantly. =(...

(As an aside, why can't you make standalone functions explicit?)

Edit: You know what they say about programmers coding too late into the night! (If you remember the joke, tell me, because I was too sleepy when I originally heard it and I've since forgotten it)

The real problem

I'm using MinGW v4.7.2, and the problem was alot different then my post originally assumed.

The problem is, I have several overloads. Yes, this example works fine:

void myFunc(const std::string &subStr);
void myFunc(char character);

But if you add an std::function overload, it breaks down:

void myFunc(const std::string &subStr);
//Not actually part of the problem; I was confused by part of the error message highlighting this function.
//void myFunc(char character); 
void myFunc(std::function<bool(char)); //<-- The real problem

My string library has std::string, char, and std::function overloads (and occasionally a few more overloads for simplifying functions with alot of optional parameters).

When I have std::function as an overload, I get this error message:

error: call of overloaded ‘myFunc(const char [15])’ is ambiguous
candidates are:
void myFunc(char) <near match>
no known conversion for argument 1 from ‘const char [15]’ to ‘char’
void myFunc(std::function<bool(char)>)
void myFunc(const string&)

The myFunc(char) was how I initially got confused last night. Removing that overload from the code, I get the error message:

error: call of overloaded ‘myFunc(const char [15])’ is ambiguous
candidates are:
void myFunc(std::function<bool(char)>)
void myFunc(const string&)

Here's a self-contained compilable example.

How can I make a string literal choose the std::string over the std::function? It's probably ambiguous because std::function's constructor is templated and is designed to take a function pointer, among other things.

Since my string library, specifically, uses only std::function<bool(char)> and std::function<bool(const std::string&)>, already typedef'd, I could inherit those into a class with an explicit constructor.

Is there other suggestions or options available?

War es hilfreich?

Lösung

Can you update your compiler? Your example compiles as expected in g++4.8 and above.

This is actually is a current defect report in the C++ standard. See 2132. std::function ambiguity. It is currently in review status, but most likely will be accepted. This will make sure that non-Callable types like your example never participate in overload resolution:

These constructors shall not participate in overload resolution unless f is Callable

Currently g++4.8 and above implements this.

Andere Tipps

You did not provide self-contained repro so it's hard to say what's wrong. But here are a couple of guesses:

  1. You have a bug in compiler (unlikely)
  2. You don't call character overload properly. You should call it like that: myFunc('c').
  3. You provided incorrect calling code or incorrect method signature(s).

I guess following code snippet should explain what should be happening and how to declare and call methods properly. Note myOtherFunc trick with capturing literal. It can be done better w/o template function, with smart wrapper around string, but I'll leave that out.

You can also try it on your compiler and see if it works, then we'll know if you have compiler problem.

Live code: http://codepad.org/gzB7xWs2

#include <string>
#include <iostream>

using namespace std;

void myFunc(char c) {
    cout << "myFunc called with char" << endl; 
}

void myFunc(const string& s) {
    cout << "myFunc called with string" << endl;
}

void myOtherFunc(char c) {
    cout << "myOtherFunc called with char" << endl; 
}

void myOtherFunc(const string& s) {
    cout << "myOtherFunc called with string" << endl;
}

template <size_t StingSizeWithNullTerminator>
void myOtherFunc(const char (&buf)[StingSizeWithNullTerminator]){
    cout << "myOtherFunc called with literal of size " << (StingSizeWithNullTerminator - 1) << endl;
}

int main() {
    myFunc("string");
    myFunc('c');
    myFunc(string("std string"));

    myOtherFunc("string");
    myOtherFunc('c');
    myOtherFunc(string("string"));

    return 0;
}

Output:

myFunc called with string
myFunc called with char
myFunc called with string
myOtherFunc called with literal of size 6
myOtherFunc called with char
myOtherFunc called with string

Update

Now, with the example, it's clear what the problem is. The problem is that there is no method with exact signature that accepts char[15]. And compiler needs to perform conversion. The problem is that it can either convert to std::string or to std::function (because std::function has template constructor which accepts any type, including char[15]). Therefore it can't choose which conversion to use and gives up.

Therefore there's no clean solution for that as far as I know of, but here are some no-so-clean:

  1. Use explicit conversion to std::string when calling methods
  2. Ask yourself (and maybe tell us), what's the reason for having myFunc which accepts both strings and functions. Maybe there's a problem with design and you can avoid having functions with same names.
  3. If you only need to accept bool(&)(char) function, you can use custom wrapper instead (see example below)

Example for 3rd option (http://ideone.com/o0NqUf):

#include <iostream>
#include <functional> //Required for std::function.

struct Callback
{
    Callback(bool (&func)(char)): m_func(func)
    {}

    bool operator()(char c) { return m_func(c); }
    bool (&m_func)(char);
};

void myFunc(Callback seperatorFunc)
{
    std::cout << "Callback overload" << std::endl;
}

void myFunc(const std::string &separator)
{
    std::cout << "std::string overload" << std::endl;
}

bool testCallback(char)
{
    return true;
}

int main()
{
    myFunc("String literal");
    myFunc(std::string("std::string"));
    myFunc(testCallback);

    return 0;
}

Output:

std::string overload
std::string overload
Callback overload

You should be able to SFINAE yourself very easily out of this one:

template <typename F>
auto myFunc(F f) -> decltype(!f('0'), std::function<bool(char)>(f), void()) {
    std::cout << "std::function<bool(char)> overload" << std::endl;
}

Or, using a C++03 compiler (possibly using tr2/type_traits or Boost Type Traits if your compiler doesn't yet have them):

template <typename F>
void myFunc(F f, typename std::enable_if<std::is_constructible<
       std::function<bool(char)>, F>::value>::type* = nullptr)
{
    std::cout << "std::function<bool(char)> overload" << std::endl;
}


Proof it works:http://ideone.com/Q87JsV

#include <iostream>
#include <type_traits>
#include <functional>

#if 1
template <typename F>
auto myFunc(F f) -> decltype(!f('0'), std::function<bool(char)>(f), void()) {
    std::cout << "std::function<bool(char)> overload" << std::endl;
}
#else
template <typename F>
void myFunc(F f, typename std::enable_if<std::is_constructible<
       std::function<bool(char)>, F>::value>::type* = nullptr)
{
    std::cout << "std::function<bool(char)> overload" << std::endl;
}
#endif


void myFunc(const std::string &seperator) {
    std::cout << "std::string overload" << std::endl;
}

bool testCallback(char) {
    return true;
}

int main()
{
    myFunc("String literal");
    myFunc(std::string("std::string"));
    myFunc(testCallback);
}

Output:

std::string overload
std::string overload
std::function<bool(char)> overload
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top