繁体   English   中英

具有模板参数的std :: function

[英]std::function with templated arguments

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

我之前尝试过定义。 但是,当我尝试将其应用于某些特定功能时,出现编译错误。

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

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

但是当你打电话

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

// ...

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

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

因此,不能将T3类型推论为bool因为要将feq转换为std::function<bool(double, double)>您必须在推论之前知道T3bool

可能的解决方案:

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

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

这样,编译器知道T3bool ,因此将feq转换为std::function<bool(double, double)>

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

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

因此编译器可以将std::function作为第三个参数,并从中推导出T3

(3)(更灵活,恕我直言,这三者中的最好的)避免std::function并为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()推断返回的向量的类型(旧的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

问题在于模板函数不会推断类型并且不会进行隐式转换(如果您不提供类型,只需让编译器生成函数即可)。 编译器只是试图找到一个简单的匹配。 考虑以下示例:

template<typename T>
T add2(T a, T b)
{
    T res = a + b;
    return res;
}


int main()
{
    int a = add2(10, 20);  // ok
    double b = add2(10.2, 20); // error, no implicit cast from int to double

    return 0;
}

main的第二个赋值不会发出no matching function for call to 'add2(double, int)'错误。

与您的情况一样,您传递的feq的类型为bool (*)(double, double) ,也就是说,它是一个函数指针,而mapzip2需要std::function对象。 模板没有隐式转换。

正如其他人建议的那样,您可以显式构建函数对象。 (也正如其他人所指出的,您需要返回vector<T3> ,而不仅仅是T3 ,但这是第二个问题,与原始问题无关)。

最后,如果您确实提供了模板类型,则编译器确实会尝试隐式转换,例如,在上面的示例中,以下代码将起作用:

double b = add2<double>(10.2, 20);

标准库通过使用迭代器解决了此问题。 最好也使用它们,因为您的代码具有与标准算法相同的结构:

// Overload #1
template<class I1, class I2, class O, class F>
void zip(I1 begin1, I1 end1, I2 begin2, O out_it, F f) {
  while (begin1 != end1) {
    out_it++ = f(*begin1++, *begin2++);
  }
}

// Overload #2
template<class C1, class C2, class R, class F>
void zip(C1& c1, C2& c2, R& ret, F f) {
  using std::begin; using std::end;
  zip(begin(c1), end(c1), begin(c2), std::back_inserter(ret), f);
}

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

或者只是使用std::transform()

如果您仍想从函数返回向量,则有助于将返回类型推导与函数本身解耦:

template<class T> using value_t = typename std::decay_t<T>::value_type;
template<class F,class... Cs> using zip_ret = std::result_of_t<F&(value_t<Cs>...)>;

template<class C1, class C2, class F, class R=zip_ret<F, C1, C2>>
std::vector<R> zip(C1& c1, C2& c2, F f) {
    using std::begin; using std::end;
    std::vector<R> ret;
    std::transform(begin(c1), end(c1), begin(c2), std::back_inserter(ret), f);
    return ret;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM