无法推断模板参数,因为我试图使"map"函数

Couldn't infer template argument in my attempt to make "map" function

本文关键字:map 因为 函数 参数      更新时间:2023-10-16

我想在c++中创建一个"map"函数,它接受一个函数和一个容器,并返回该容器,但包含由函数返回的类型元素,这些元素应该是将函数应用于容器参数元素的结果。

例如,我们应该能够这样做:

map([](int i){return float(i + 1);}, vector<int> {1, 2, 3});  // vector<float> {2.0, 3.0, 4.0}

这是我的尝试实现它,但看起来像我不完全理解模板编程:http://ideone.com/zMYCVw

#include <iostream>
#include <functional>
#include <string>
#include <list>
#include <vector>
template <
    typename FunctionT,
    template <typename> class ContainerT,
    typename ElemT,
    class FunctionResultT = typename std::result_of<FunctionT>
>
ContainerT<FunctionResultT> map(
    FunctionT func,
    const ContainerT<ElemT>& container
){
    ContainerT<FunctionResultT> resultContainer;
    for (const auto& elem : container){
        resultContainer.push_back(func(elem));
    }
    return resultContainer;
}

template <typename T>
T increaseByOne(const T& number){
    return number + 1;
}
template <typename T>
std::string numberToString(const T& number){
    return std::to_string(number);
}

int main(){
    const auto theList = std::list<float> {1.0, 2.0, 3.0, 4.0, 5.0};
    for (const auto& ele : map(numberToString, map(increaseByOne, theList))){
        std::cout << ele << std::endl;
    }
}

编译器错误:

file.cpp:39:48: error: no matching function for call to 'map'
    for (const auto& ele : map(numberToString, map(increaseByOne, theList))){
                                           ^~~
file.cpp:13:29: note: candidate template ignored: couldn't infer template argument 'FunctionT'
ContainerT<FunctionResultT> map(
                        ^
1 error generated.

我发现了导致问题的几个因素。以下是我的发现:

  • 函数模板表示无限重载集。函数的类型需要在将其作为参数传递时知道。这意味着需要显式地指定它的模板参数。也就是说,这些需要修改:

    map(numberToString, map(increaseByOne, theList)
    //  ^^^^^^^^^^^^^^      ^^^^^^^^^^^^^
    

    :

    map(numberToString<float>, map(increaseByOne<float>, theList)
    //                ^^^^^^^                   ^^^^^^^
    
  • std::list接受多个模板参数。其余的都是默认的。当您将模板参数定义为:

    template<typename> class ContainerT,
    

    意味着ContainerT只能接受一个模板参数。当编译器看到你试图从std::list的实例中推断出这种类型时,它会看到模板参数的数量不匹配。然后类型演绎失败。c++ 11有可变参数包,你可以使用它们来使ContainerT接受任意数量的模板参数:

    template<typename...> class ContainerT,
    
  • 最后,您没有为FunctionResultT提供type。你可以在这里设置:

    class FunctionResultT = typename std::result_of<FunctionT>::type
    //                                                        ^^^^^^
    

    但是注意,这将失败,因为std::result_of是一个模板,只有当其模板实参是函子时才提供::type成员。这基本上意味着,如果它是一个具有重载operator()成员函数的类。

    您可以将FunctionResultT默认为函数调用的类型,而不是使用std::result_of。这样的:

    class FunctionResultT = decltype(std::declval<FunctionT>()(std::declval<ElemT>()));
    

现在这是我所做的更改的结果:

template <
    typename FunctionT,
    template <typename...> class ContainerT,
    typename ElemT,
    class FunctionResultT = decltype(std::declval<FunctionT>()(std::declval<ElemT>()))
>
template<class OutputContainer, class InputContainer, typename Fn>
OutputContainer map(Fn fn, InputContainer input_container){
    OutputContainer output;
    for(auto&i:input_container)
        output.push_back(fn(i));
    return output;
}