3

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?

Vishal Vasani
  • 647
  • 8
  • 16
bitmask
  • 32,434
  • 14
  • 99
  • 159

2 Answers2

2

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.)

Enlico
  • 23,259
  • 6
  • 48
  • 102
  • Looks like an option, but using macros always does makes me a bit uncomfortable. – bitmask Jun 22 '22 at 12:11
  • If used more than one time, then `BOOST_HOF_LIFT` _does_ become more concise than writing the lambdas manually. – Ted Lyngmo Jun 22 '22 at 12:11
  • @bitmask, macros is the only non-verbose tool that you have at your disposal in this case. If you want a non-macro solution to pass an overloaded and/or templated entity to a function call, I'm afraid you need to make a proposal for the standard. – Enlico Jun 22 '22 at 12:15
  • 1
    @Enlico I don't disagree. – bitmask Jun 22 '22 at 12:16
1
  • 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.

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108