我有一个程序,我必须在对每个组件进行一些计算后在屏幕上打印许多STL向量。所以我尝试创建一个这样的函数:
template <typename a>
void printWith(vector<a> foo, a func(a)){
for_each(foo.begin(), foo.end(), [func](a x){cout << func(x) << " "; });
}
然后像这样使用它:
int main(){
vector<int> foo(4,0);
printWith(foo, [](int x) {return x + 1;});
return 0;
}
不幸的是,我有一个关于我在printWith
调用中放入的lambda表达式类型的编译错误:
g++ -std=gnu++0x -Wall -c vectest.cpp -o vectest.o
vectest.cpp: In function ‘int main()’:
vectest.cpp:16:41: error: no matching function for call to ‘printWith(std::vector<int>&, main()::<lambda(int)>)’
vectest.cpp:10:6: note: candidate is: void printWith()
make: *** [vectest.o] Error 1
当然,如果我这样做:
int sumOne(int x) {return x+1;}
然后printWith(foo, sumOne);
按预期工作。我认为lambda表达式的类型将是具有推断返回类型的函数的类型。我也认为我可以在任何可以适合普通功能的地方使用lambda。我如何使这项工作?
答案 0 :(得分:6)
以下适用于我:
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
template <typename a, typename F>
void printWith(vector<a> foo, F f){
for_each(foo.begin(), foo.end(), [&](a x){cout << f(x) << " "; });
}
int main(){
vector<int> foo = {1,2,3,4,5};
printWith(foo, [](int x) {return x + 1;});
std::cout << '\n';
return 0;
}
测试:
$ g++-4.5 -std=gnu++0x -Wall test.cpp
$ ./a.out
2 3 4 5 6
或者,您可以利用没有lambda-capture can be implicitly converted的闭包类型来实现指针。这更接近原始代码,并且还减少了函数模板的实例化数量(在原始解决方案中,每次使用具有不同函数对象类型的函数模板时,您都会获得新的实例化;请注意,它不会在这种特殊情况下很重要,因为printWith
函数非常短,并且很可能总是内联的):
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
template <typename a, typename b>
void printWith(const vector<a>& foo, b f(a)){
for_each(foo.begin(), foo.end(), [=](a x){cout << f(x) << " "; });
}
int main(){
vector<int> foo = {1,2,3,4,5};
printWith<int, int>(foo, [](int x) {return x + 1;});
std::cout << '\n';
return 0;
}
不幸的是,隐式转换在模板参数推断方面效果不佳:正如您所看到的,我必须在调用printWith
时指定模板参数。
另一种方法是使用std::function
。这也有助于最小化模板实例化的数量,甚至对于具有lambda-capture的lambda表达式也有效,但是模板参数推导具有相同的问题:
#include <algorithm>
#include <functional>
#include <iostream>
#include <vector>
using namespace std;
template <typename a, typename b>
void printWith(const vector<a>& foo, std::function<b(a)> f){
for_each(foo.begin(), foo.end(), [&](a x){cout << f(x) << " "; });
}
int main(){
vector<int> foo = {1,2,3,4,5};
int y = 1;
printWith<int, int>(foo, [&](int x) { return x + y; });
std::cout << '\n';
return 0;
}
答案 1 :(得分:5)
您遇到问题的原因是您尝试使用某项功能。自由函数具有特定表示(作为函数指针),其不能与任何类型的函数对象互换。应避免使用函数指针(基本上就是这里)。您需要使用模板指定的类型直接获取函数对象。
template <typename a, typename Func>
void printWith(vector<a> foo, Func func){
for_each(foo.begin(), foo.end(), [&](a x){cout << func(x) << " "; });
}
或者,使用多态函数对象,例如std::function
。
template<typename a>
void printWith(vector<a> foo, std::function<string(const a&)> func) {
for_each(foo.begin(), foo.end(), [&](a x) { cout << func(x) << " "; });
}
答案 2 :(得分:3)
void printWith(vector<a> foo, b func(a)){
这是错误的,你不能这样做,这使得编译器没有考虑到这段代码,因为它无效。
您有两种解决方法:
1)不要求参数类型,只需要一个仿函数:
void printWith(vector<a> foo, b func ){ // keep the rest of the code the same
如果func不接受a
作为参数,则函数的其余部分将无法编译。
2)强制仿函数类型:
template <typename a>
void printWith(vector<a> foo, std::function< void (a) > func ){
然后就像你使用的是函数指针。没有(或更少)编译时优化,但至少你强制执行仿函数签名。有关详细信息,请参阅std :: function或boost :: function。
答案 3 :(得分:2)
这不起作用的原因是你将模板参数推导与隐式转换混合在一起。如果你摆脱扣除它的工作原理:
printWith<int>(foo, [](int x) {return x + 1;});
然而,让func
的类型成为另一个模板参数会更好(在printWith内部),正如其他人推荐的那样。
另一方面,如果你真的想为这种类型添加约束,有更好的方法可以使用SFINAE(用于软错误)或static_assert
(用于硬错误)。
例如:
// A constraints metafunction
template<typename T, typename Element>
struct is_element_printer
: std::is_convertible<T, Element (*)(Element)>
{};
此处,is_element_printer<T, Element>::value
为true
iff T
隐式转换为Element (*)(Element)
。我只是将它用于说明目的,我不能推荐它用于实际用途:在许多不是函数指针的情况下,有很多东西可以作为“元素打印机”。我只是这样做是因为std::is_convertible
可以从<type_traits>
获得,并且没有其他更明显的测试可用。你应该自己写。
然后:
template<typename Container, typename Functor>
void
printWith(Container&& container, Functor&& functor)
{
// avoid repetition
typedef typename std::decay<Container>::type::value_type value_type;
// Check our constraints here
static_assert(
std::is_element_printer<
typename std::decay<Functor>::type,
value_type
>::value,
"Descriptive error message here"
);
// A range-for is possible instead
std::for_each(container.cbegin(), container.cend(), [&functor](value_type const& v)
{ std::cout << functor(v) << ' '; });
}