c++ - 带有模板参数的 std::function

标签 c++ c++11 templates functional-programming template-argument-deduction

我想编写一个模板函数,将某些函数应用于来自两个 vector 的元素对。结果应该是一个新的结果 vector 。我希望这是一个模板化函数,以便它适用于不同类型。

我之前试过这个定义。但是,当我尝试将它应用于某些特定函数时,出现编译错误。

#include <vector>
#include <cmath>
#include <iostream>
#include <functional>

using namespace std;

template<typename T1, typename T2, typename T3>
vector<T3> mapzip2(const vector<T1> &xs, const vector<T2> &ys, std::function<T3(T1, T2)> l) {
    if (xs.size() != ys.size())
        throw runtime_error("mapzip2: container sizes (" + to_string(xs.size()) + 
                            " and " + to_string(ys.size()) + ") do not match");
    vector<T3> result;
    result.reserve(xs.size());
    for (int i = 0; i < xs.size(); i++)
        result.push_back(l(xs[i], ys[i]));

    return result;
}

constexpr double PRECISION = 1E-6;

bool feq(double a, double b) {
    return abs(a - b) < PRECISION;
}

int main() {
    vector<double> a = {0.3, 0.42, 0.0, -7.34};
    vector<double> b = {0.3, 0.42, 0.0, -7.34};

    // compilation error: no matching function for call to 
    // ‘mapzip2(std::vector<double>&, std::vector<double>&, bool (&)(double, double))’
    vector<bool> result = mapzip2(a, b, feq);

    for (bool b: result) cout << b << ' ';
    cout << endl;
}

类型推导有什么问题?

最佳答案

你遇到了一个先有鸡还是先有蛋的问题。

T3输入

template<typename T1, typename T2, typename T3>
T3 mapzip2(const vector<T1> &xs, const vector<T2> &ys, std::function<T3(T1, T2)> l)

必须从第三个参数推导出来,a std::function<T3(T1, T2)>

但是当你打电话的时候

bool feq(double a, double b) {
    return abs(a - b) < PRECISION;
}

// ...

    vector<bool> result = mapzip2(a, b, feq);

你调用mapzip()feq可以转换为 std::function<bool(double, double)>不是 std::function<bool(double, double)>

所以 T3类型不能推断为 bool因为要转换 feqstd::function<bool(double, double)>你必须知道,在推导之前T3bool .

可能的解决方案:

(1) 显式模板类型调用mapzip()

vector<bool> result = mapzip2<double, double, bool>(a, b, feq);

这样编译器就知道T3bool , 所以转换 feqstd::function<bool(double, double)>

(2) 构造一个std::function<bool(double, double)>feq

vector<bool> result = mapzip2(a, b, std::function<double, double, bool>{feq});

所以编译器可以接收到一个std::function作为第三个参数并推导出T3从它。

(3)(更灵活,恕我直言,三者中最好的)避免 std::function并为函数使用更通用的函数类型名

template<typename T1, typename T2, typename F>
auto mapzip2(const vector<T1> &xs, const vector<T2> &ys, F l) {
    if (xs.size() != ys.size())
        throw runtime_error("mapzip2: container sizes (" + to_string(xs.size()) + 
                            " and " + to_string(ys.size()) + ") do not match");

    vector<decltype(l(xs[0], ys[0]))> result; // <-- use decltype() !

    result.reserve(xs.size());
    for (int i = 0; i < xs.size(); i++)
        result.push_back(l(xs[i], ys[i]));

    return result;
}

观察 decltype() 的使用推断返回 vector 的类型(旧的 T3 )和 auto 的使用(从 C++14 开始)用于函数的返回类型。

如果你不能使用C++14(只能使用C++11),你必须添加尾随返回类型

template<typename T1, typename T2, typename F>
auto mapzip2(const vector<T1> &xs, const vector<T2> &ys, F l) 
    -> std::vector<decltype(l(xs[0], ys[0]))>
 {

 }

还请注意——正如 ypnos 在评论中指出的那样——你的原始签名 mapzip2()错了:你返回 result , 一个 std::vector<T3> , 不是 T3 .

关于c++ - 带有模板参数的 std::function,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57375532/

相关文章:

c++ - 与 (N)RVO 一起有效使用 move 语义

c++ - 生成保证顺序执行吗?

C++ 错误 : no matching function for call to

C++ TCHAR[] 到字符串

c++ - 仅为基本数据类型制作模板

c++ - 双模板函数重载失败

c++ - 'this' 参数的类型为 const 但函数未标记为 const C++ 重载运算符

c++ - 如何有效地将 std::vector 视为 C 缓冲区?

c++ - 显式转换模板非类型参数时出错

使用非类型模板参数的 C++ 优化