为什么下面的 std::transform 示例需要函数指针而不是函数对象?

Why does the following `std::transform` example need a function pointer instead of a function object?

函数模板std::transform()接受一个范围,使用运算符对其进行组件操作,并将结果保存在另一个范围中。在下面的示例中,该函数接受一个名为 nl 的通用 std::initializer_list 并使用 (std::string (*)(T)) std::to_string 对其进行操作以将其所有条目转换为字符串,然后将结果存储在一个名为buffer.

class num_list
{
public:
    template<typename T>
    num_list(std::initializer_list<T> nl):
        size{nl.size()},
        buffer(new std::string[size])
    {
        std::transform(nl.begin(), nl.end(), // input sequence
        buffer,                              // output result
        (std::string (*)(T))std::to_string); // unary operator
    }
    //...
private:
    std::size_t size;
    std::string * buffer;
};

我想知道为什么我们需要将 std::to_string 类型转换为函数指针才能使其工作。如果我们放弃对函数指针类型 (std::string (*)(T)) 的强制转换,为什么代码无法使用 C++11 进行编译?我无法解读编译器抛出的抱怨。

error: no instance of overloaded function "std::transform" matches the argument list

std::transform 是一个函数模板,它通过模板参数类型获取函数对象。由于类型是模板参数,因此必须推导它。 std::to_string 是一个重载函数,因此当您尝试推断其类型时,您会得到多个结果。由于推导中没有其他任何东西可以帮助缩小类型范围,编译器将无法推导出 std::to_string 的类型,您将得到一个错误。

当您将 std::to_string 转换为 std::string (*)(T) 时,您现在只有单一类型供编译器推断,它确实如此,并且您可以成功编译。

您可以使用通用 lambda 代替转换,而不是像

那样转发到 std::to_string
std::transform(nl.begin(), nl.end(), // input sequence
buffer,                              // output result
([](auto&& val){ return std::to_string(val);});

但这至少需要 C++14。对于 C++11,您可以使用 T 作为参数类型,例如

std::transform(nl.begin(), nl.end(), // input sequence
buffer,                              // output result
([](const T& val){ return std::to_string(val);});