std::transform with an overloaded conversion function

Viewed 339

I want to transform all objects - which are of type, say, char - from one container c and store the results - which are of type foo - in a different container v. The desired conversion function is an overloaded function - call it to_foo.

Consider the following example:

#include <algorithm>
#include <vector>    

struct foo {};

foo to_foo(char) { return {}; }
foo to_foo(int) { return {}; }

int main()
{
    std::array<char, 1> c;
    std::vector<foo> v(c.size());

//  cannot determine which instance of overloaded function "to_foo" is intended:
    std::transform(c.begin(), c.end(), v.begin(), to_foo);

//  ok:
    std::transform(c.begin(), c.end(), v.begin(), [](auto const& ch) { return to_foo(ch); });
}

While it's somehow plausible to me that in the first transform the compiler doesn't know which overload of to_foo it should take (on the other, couldn't he deduce it from the value_type of the iterators?), I don't understand why he is able to do so in the second transform. What's happening here?

1 Answers

You have to explicitly tell it which overload to use by casting to_foo:

std::transform(c.begin(), c.end(), v.begin(), static_cast<foo (*)(char)>(to_foo));

The type of the unary operation parameter of std::transform is a different template argument than the one used for the iterators. So it has to use the type of the value you pass to deduce the template argument. to_foo's type is ambiguous, so it can't complete that process. Casting the function pointer to a specific overload type makes it unambiguous.


In the second form, the lambda is a callable object, and the use of auto makes the object's () operator a function template:

struct NamelessGenericLambda {
    template <typename T>
    foo operator ()(T const& ch) { return to_foo(ch); }
};
std::transform(c.begin(), c.end(), v.begin(), NamelessGenericLambda{});

The type is clear so it can instantiate the std::transform template.

Then somewhere inside std::transform it will try to instantiate the operator () template by passing a char. Because it passes an unambiguous type, the compiler deduces ch is a char. Then it picks the right overload of to_foo using regular overload resolution.

Related