由于模板类型扣除的问题,此代码无法编译,甚至在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
}
答案 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)
错误来自编译器试图推导出T而不能推导T
std::function
参数传递一个lambda。
标准使用普通模板参数来表示这样的谓词。
2.1使用模板参数,谓词最通用。
你可以传递std::function
,std::bind
,函数指针,lambdas,functor ......
2.2内联(如果可能)最有可能发生。
有了一大堆运气,编译器足够聪明,可以内联一个lambda尽管被传递了#34;通过&#34; std::function
进入模板,但我不打赌。相反,如果我通过自己的类型传递lambda,我实际上期望编译器内联一个lambda(如果合适的话)。
您的代码还有其他一些问题。
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();)
,这样我们就会在a
或b
中留下元素。
如果您在表现和普遍性之后,可能会想要写下这些内容:
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())