How can I specify an overload when passing an overloaded function to another?

Viewed 95

Simple example:

std::vector<std::string> strings{"1","2","3");
std::vector<double> doubles(3);
transform(begin(strings), end(strings), begin(doubles), std::stod);

This fails to compile because the compiler cannot decide which std::stod to use. The one that takes an std::string or the one that takes an std::wstring. We see that only one would work given how std::transform works, but the compiler doesn't.

Options (bad ones)

  • Now, we cannot say std::stod<std::string> because std::stod is not a template but an overload. And even if it was a template, it would have to be a class template with a static non-templated function: Stod<std::string>::stod.
  • What I usually do is use a lambda: [](std::string const& s) { return std::stod(s); } but that is quite a lot of code for nothing.
  • There is also the option to cast the overloaded function to the specific type (double (*)(std::string const&)) but to do that, I have to know the return type. For std::stod that is simple, because it always returns a simple double but for other functions that might be a very complicated thing to write down.

Is there a more concise way than the lambda to select the correct overload or trick the compiler into figuring it out itself?

2 Answers

As you already know, you either resolve the overload upfront by casting, i.e. choosing one of the overloads, or you delay the overload resolution by wrapping the call in a function object (being it a lambda or something else).

The quickest way to take the latter approach, without writing any code of yours, is Boost.Hof's BOOST_HOF_LIFT.

So instead of passing this

std::stod

you pass this

BOOST_HOF_LIFT(std::stod)

Isn't it concise enough?

(Working example on Compiler Explorer.)

  • You would need to help the compiler to select overload:
    static_cast<double(*)(const std::string&, std::size_t*)>(std::stod)
    
  • This does not work anyway. std::stod requires 2 arguments. The default value for the argument doesn't tag along with the function pointer you give to transform to work with.
  • You also take the address of a function in the standard library which isn't allowed (with a few exceptions - but std::stod is not one of them).

The simplest solution for all is to package it in a lambda:

std::transform(std::begin(strings), std::end(strings), std::begin(doubles),
               [](const std::string& s) { return std::stod(s); });

Is there a more concise way than the lambda to select the correct overload or trick the compiler into figuring it out itself?

No, none that is portable.

Related