C++14 中的模板化函数参数
templated function argument in C++14
由于模板类型推断的问题,此代码无法编译,即使在 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
}
这里的问题是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 还是函子都无关紧要。
你需要以某种方式使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)
-
错误来自编译器试图推断 T,而它无法推断传递 lambda 的
std::function
参数的T
。 -
该标准对此类谓词使用普通模板参数是有充分理由的。
2.1 谓词使用模板参数是最通用的。
你可以传入
std::function
、std::bind
、函数指针、lambda、函子...2.2 内联(如果可能)最有可能发生。
运气好的话,编译器足够聪明,可以内联一个lambda,尽管它被"通过"
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;
}
因为我不能评论:一般来说@NathanOliver说什么。lambda expression
不能"投射"到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
.请参阅问题 使用"类"或"类型名称"作为模板参数?以及此处的旧 MSDN 文章。
另外,请注意您在第 13 行有一个拼写错误。你可能的意思是:
for (;;ia!=a.end() || ib!=b.end())
- static_assert在宏中,但也可以扩展到可以用作函数参数的东西
- C++中的高效循环缓冲区,它将被传递给C样式数组函数参数
- 当从函数参数中的临时值调用复制构造函数时
- 如何从"decltype()"获取函数参数的数量<funtion>?
- 如何将lambda作为模板类的成员函数参数
- 模板参数推导失败,函数参数/参数不匹配
- 如何在C++中将迭代器作为函数参数传递
- 将函数参数"const char*"转换为"std::string_view"是
- C++ 如何将数组值解压缩为函数参数
- 主函数参数的属性
- 具有两个间接寻址运算符 (C++) 的函数参数的用途
- "Warning: Comma within array index expression"但逗号分隔函数参数
- 如何定义在用作函数参数时工作的类模板的转换
- 将函数参数完美转发到函数指针:按值传递呢?
- 为什么我不能将引用作为 std::async 的函数参数传递
- 什么..(省略号)作为函数原型中唯一的函数参数,C++?
- 是否可以就地构造一个固定大小的数组作为函数参数?
- 接受模板作为函数参数
- 将成员函数作为构造函数参数调用时出错 "Variable is not a type name"
- Arduino 函数参数