C ++中的模板化函数参数14

时间:2017-03-08 13:49:49

标签: c++ templates lambda c++14 std-function

由于模板类型扣除的问题,此代码无法编译,甚至在C ++ 14下也无法编译。什么是最不优雅的解决方法?

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

template <class T>
std::vector<T> merge_sorted(
    const std::vector<T>& a, const std::vector<T>& b,
    std::function<bool(const T, const T)> a_before_b)
{
    std::vector<T> ret;
    auto ia=a.begin();
    auto ib=b.begin();
    for (;;ia!=a.end() || ib!=b.end())
        ret.push_back( a_before_b(*ia,*ib) ? *(ia++) : *(ib++) );
    return ret;
}

int main()
{
    std::vector<double> A { 1.1, 1.3, 1.8 };
    std::vector<double> B { 2.1, 2.2, 2.4, 2.7 };
    auto f = [](const double a, const double b) -> bool {
        return (a-(long)(a))<=(b-(long(b))); };
    std::vector<double> C = merge_sorted(A, B, f);
    for (double c: C)
        std::cout << c << std::endl;
    // expected outout: 1.1 2.1 2.2 1.3 2.4 2.7 1.8
}

来自g++ -std=c++14 main.cpp的错误消息:

main.cpp: In function ‘int main()’:
main.cpp:23:49: error: no matching function for call to ‘merge_sorted(std::vector<double>&, std::vector<double>&, main()::<lambda(double, double)>&)’
     std::vector<double> C = merge_sorted(A, B, f);
                                                 ^
main.cpp:6:16: note: candidate: template<class T> std::vector<T> merge_sorted(const std::vector<T>&, const std::vector<T>&, std::function<bool(T, T)>)
 std::vector<T> merge_sorted(
                ^~~~~~~~~~~~
main.cpp:6:16: note:   template argument deduction/substitution failed:
main.cpp:23:49: note:   ‘main()::<lambda(double, double)>’ is not derived from ‘std::function<bool(T, T)>’
     std::vector<double> C = merge_sorted(A, B, f);

==

稍后编辑,只是为了记录:这里有一个代码的版本,编译(感谢收到的答案)并且正确执行(对上述未经测试的代码进行了几次修正):

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

template <class T, class Pred>
std::vector<T> merge_sorted(const std::vector<T>& a, const std::vector<T>& b, Pred a_before_b)
{
    std::vector<T> ret;
    auto ia=a.begin();
    auto ib=b.begin();
    for (;ia!=a.end() && ib!=b.end();)
        ret.push_back( a_before_b(*ia,*ib) ? *(ia++) : *(ib++) );
    for (;ia!=a.end();)
        ret.push_back( *(ia++) );
    for (;ib!=b.end();)
        ret.push_back( *(ib++) );
    return ret;
}

int main()
{
    std::vector<double> A { 1.1, 1.3, 1.8 };
    std::vector<double> B { 2.1, 2.2, 2.4, 2.7 };
    auto f = [](const double a, const double b) -> bool {
        return (a-(long)(a))<=(b-(long(b))); };
    std::vector<double> C = merge_sorted(A, B, f);
    for (double c: C)
        std::cout << c << std::endl;
    // expected outout: 1.1 2.1 2.2 1.3 2.4 2.7 1.8
}

4 个答案:

答案 0 :(得分:3)

您需要以某种方式将a_brefore_b的类型设为非推断的上下文。我通常会为此引入一个名称恰当的助手:

template <class T>
struct NonDeduced
{
  using type = T;
};

template <class T>
std::vector<T> merge_sorted(
    const std::vector<T>& a, const std::vector<T>& b,
    typename NonDeduced<std::function<bool(const T, const T)>>>::type a_before_b)

当然(正如@Marc Glisse在评论中指出的那样),首先强制使用std::function作为a_before_b的类型是非常必要的。更不用说它很容易带来性能损失(std::function在内部使用类型擦除和动态调度)。只需遵循标准库的功能,并通过模板参数键入谓词:

template <class T, class Pred>
std::vector<T> merge_sorted(
    const std::vector<T>& a, const std::vector<T>& b,
    Pred a_before_b)

答案 1 :(得分:3)

此处的问题是f不是std::function。它是一些未命名的类类型,但它不是std::function。当编译器执行模板参数推导时,它不会进行任何转换,它可以使用参数来推断它们的类型。这意味着它期望看到std::function<bool(const T, const T)>它看到main()::<lambda(double, double)>,因为它是lambda的类型,并且因为这些类型不匹配,所以推导失败。为了使扣除成功,你需要让它们匹配。

在不更改功能签名的情况下,您必须将f强制转换为std::function才能使其正常工作。那看起来像是

std::vector<double> C = merge_sorted(A, B, static_cast<std::function<bool(const double,const double)>>(f));

如果您不介意更改功能签名,那么我们可以使用

template <class T, class Func>
std::vector<T> merge_sorted(
    const std::vector<T>& a, const std::vector<T>& b,
    Func a_before_b)

现在,如果您传递std::function或lambda或仿函数并不重要。

答案 2 :(得分:0)

  1. 错误来自编译器试图推导出T而不能推导T std::function参数传递一个lambda。

  2. 标准使用普通模板参数来表示这样的谓词。

    2.1使用模板参数,谓词最通用。

    你可以传递std::functionstd::bind,函数指针,lambdas,functor ......

    2.2内联(如果可能)最有可能发生。

    有了一大堆运气,编译器足够聪明,可以内联一个lambda尽管被传递了#34;通过&#34; std::function进入模板,但我不打赌。相反,如果我通过自己的类型传递lambda,我实际上期望编译器内联一个lambda(如果合适的话)。

  3. 您的代码还有其他一些问题。

    3.1 for (;;ia!=a.end() || ib!=b.end())此处;设置不正确。

    3.2即使正确设置;,谓词也是错误的,因为ia!=a.end() || ib!=b.end()将保持循环运行,尽管ia == a.end()ib == b.end()为真。在循环中,如果我们已经超过最后一个元素,那么两个迭代器都被解引用以检查谓词,该谓词将我们带入未定义的行为区域。因此,循环条件必须为for (;ia!=a.end() && ib!=b.end();),这样我们就会在ab中留下元素。

  4. 如果您在表现和普遍性之后,可能会想要写下这些内容:

    template <class InIt, class OutIt, class Predicate>
    auto merge_sorted(InIt first1, InIt last1, InIt first2, InIt last2, 
        OutIt dest, Predicate pred)
    {
        // as long as we have elements left in BOTH ranges
        for (;first1 != last1 && first2 != last2; ++dest)
        {
            // check predicate which range offers the lowest value
            // and insert it
            if (pred(*first1, *first2)) *dest = *(first1++);
            else *dest = *(first2++);
        }
        // here either first1 == last1 or first2 == last2 is true
        // thus we can savely copy the "rest" of both ranges 
        // to dest since we only have elements in one of them left anyway
        std::copy(first1, last1, dest);
        std::copy(first2, last2, dest);
        return pred;
    }
    

答案 3 :(得分:-2)

由于我无法评论:一般来说@NathanOliver说的是什么。 lambda expression不能&#34;演员&#34;到std::function,因为它是 - 内部 - 一种不同的构造。 当然,如果编译器可以推断(通过静态分析)它必须为lambda创建一个std::function对象,那将是很好的。但这似乎并不是C ++ 11 / C ++ 14的一部分。

要解决此问题,我发现向模板添加typename最简单:

template <class T, typename F>
std::vector<T> merge_sorted(
    const std::vector<T>& a, const std::vector<T>& b,
    F& a_before_b)

当然您也可以使用class。请参阅问题Use 'class' or 'typename' for template parameters?the old MSDN article here

另外,请注意第13行有拼写错误。您可能意味着:

    for (;;ia!=a.end() || ib!=b.end())