将lambda作为模板函数参数传递

时间:2017-01-29 12:28:35

标签: c++ c++11 templates lambda

为什么下面的代码没有编译(在C ++ 11模式下)?

#include <vector>

template<typename From, typename To>
void qux(const std::vector<From>&, To (&)(const From&)) { }

struct T { };

void foo(const std::vector<T>& ts) {
    qux(ts, [](const T&) { return 42; });
}

错误消息是:

prog.cc:9:5: error: no matching function for call to 'qux'
    qux(ts, [](const T&) { return 42; });
    ^~~
prog.cc:4:6: note: candidate template ignored: could not match 'To (const From &)' against '(lambda at prog.cc:9:13)'
void qux(const std::vector<From>&, To (&)(const From&)) { }
     ^

但它没有解释为什么它与参数不匹配。

如果我将qux作为非模板函数,将From替换为T,将To替换为int,则会进行编译。

6 个答案:

答案 0 :(得分:13)

lambda函数不是正常函数。在任何情况下,每个lambda都有自己的类型 To (*)(const From&) 在您的情况下,非捕获lambda可以使用:

衰减到qux(ts, +[](const T&) { return 42; });
#include <vector>

template<typename From, typename To>
void qux(const std::vector<From>&, To (&)(const From&)) { }

struct T { };

void foo(const std::vector<T>& ts) {
    qux(ts, *+[](const T&) { return 42; });
}

int main() {}

如评论中所述,你可以做的最好的事情是从lambda中取出它:

aBytes = b'\x00\x47\x40\x00\x13\x00\x00\xb0'
print (aBytes)
print (''.join ([hex (aByte) for aByte in aBytes]))

注意:我认为推断返回类型和参数类型对于实际问题是必需的。否则你可以很容易地将整个lambda推断为通用的可调用对象并直接使用它,不需要腐烂任何东西。

答案 1 :(得分:10)

如果您不需要使用推导的To类型,则可以推断出整个参数的类型:

template<typename From, typename F>
void qux(const std::vector<From>&, const F&) { }

答案 2 :(得分:6)

如果我错了,请纠正我,但模板参数扣除仅推断出确切的类型而不考虑可能的转换。

因此,编译器无法为To推导出FromTo (&)(const From&),因为qux需要对函数的引用,但是您提供了一个具有自己类型的lambda。

答案 3 :(得分:2)

你绝对没有机会编译猜测To是什么。因此,您需要明确指定它。

此外,lambda需要通过指针传递。

最后,这个版本编译好了:

template<typename From, typename To>
void qux(const std::vector<From>&, To (*)(const From&)) { }

struct T { };

void foo(const std::vector<T>& ts) {
    qux<T,int>(ts,[](const T&) { return 42; });
}

答案 4 :(得分:2)

您期望两种隐式类型转换(从未命名的函数对象类型到函数引用类型)和模板类型推导都会发生。但是,you can't have both,因为您需要知道目标类型以找到合适的转换序列。

答案 5 :(得分:1)

  

但它没有解释为什么它与参数不匹配。

模板推导尝试完全匹配类型。如果无法推断出类型,则扣除失败。从不考虑转换。

在这个表达式中:

qux(ts, [](const T&) { return 42; });

lambda表达式的类型是一些唯一未命名类型。无论那种类型是什么,它绝对不是To(const From&) - 因此扣除失败。

  

如果我将qux作为非模板函数,将From替换为T,将To替换为int,则会进行编译。

事实并非如此。但是,如果参数是指针而不是引用来运行,那么它就是。这是因为没有捕获的lambda可以隐式转换为等效的函数指针类型。允许在演绎的范围之外进行此转换。

template <class From, class To>
void func_tmpl(From(*)(To) ) { }

void func_normal(int(*)(int ) ) { }

func_tmpl([](int i){return i; });   // error
func_tmpl(+[](int i){return i; });  // ok, we force the conversion ourselves,
                                    // the type of this expression can be deduced
func_normal([](int i){return i; }); // ok, implicit conversion

这就是失败的原因:

template <class T> void foo(std::function<T()> );
foo([]{ return 42; }); // error, this lambda is NOT a function<T()>

但这成功了:

void bar(std::function<int()> );
bar([]{ return 42; }); // ok, this lambda is convertible to function<int()>

首选方法是推断出可调用类型,并使用std::result_of选择结果:

template <class From,
    class F&&,
    class To = std::result_of_t<F&&(From const&)>>
void qux(std::vector<From> const&, F&& );

现在你可以正常传递你的lambda,函数或函数对象。