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

时间:2022-08-14 18:52:52

Why doesn't the following code compile (in C++11 mode)?

为什么以下代码没有编译(在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; });
}

The error message is:

错误消息是:

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&)) { }
     ^

But it doesn't explain why it couldn't match the parameter.

但它并不能解释为什么它与参数不匹配。

If I make qux a non-template function, replacing From with T and To with int, it compiles.

如果我使qux成为非模板函数,将From替换为T,将To替换为int,则编译。

6 个解决方案

#1


13  

A lambda function isn't a normal function. Each lambda has its own type that is not To (&)(const From&) in any case.
A non capturing lambda can decay to To (*)(const From&) in your case using:

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

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

As noted in the comments, the best you can do to get it out from a lambda is this:

正如评论中所指出的,从lambda中获取它的最佳方法是:

#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() {}

Note: I assumed that deducing return type and types of the arguments is mandatory for the real problem. Otherwise you can easily deduce the whole lambda as a generic callable object and use it directly, no need to decay anything.

注意:我认为推断返回类型和参数类型对于实际问题是必需的。否则,您可以轻松地将整个lambda推断为通用可调用对象并直接使用它,无需任何衰减。

#2


10  

If you don't need to use the deduced To type, you can just deduce the type of the whole parameter:

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

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

#3


6  

Correct me if I am wrong, but template parameters deduction deduces only exact types without considering possible conversions.

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

As a result the compiler cannot deduce To and From for To (&)(const From&) because qux expects a reference to function, but you provide a lambda which has its own type.

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

#4


2  

You have left absolutely no chance to compiler to guess what is To. Thus, you need to specify it explicitly.

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

Also, lambda here needs to be passed by pointer.

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

Finally, this version compiles ok:

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

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; });
}

#5


2  

You're expecting both implicit type conversions (from unnamed function object type to function reference type) and template type deduction to happen. However, you can't have both, as you need to know the target type to find the suitable conversion sequence.

您期望发生隐式类型转换(从未命名的函数对象类型到函数引用类型)和模板类型推导。但是,您不能同时拥有这两者,因为您需要知道目标类型以找到合适的转换序列。

#6


1  

But it doesn't explain why it couldn't match the parameter.

但它并不能解释为什么它与参数不匹配。

Template deduction tries to match the types exactly. If the types cannot be deduced, deduction fails. Conversions are never considered.

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

In this expression:

在这个表达式中:

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

The type of the lambda expression is some unique, unnamed type. Whatever that type is, it is definitely not To(const From&) - so deduction fails.

lambda表达式的类型是一些唯一的,未命名的类型。无论那种类型是什么,它绝对不是To(const From&) - 所以演绎失败了。


If I make qux a non-template function, replacing From with T and To with int, it compiles.

如果我使qux成为非模板函数,将From替换为T,将To替换为int,则编译。

That is not true. However, if the argument was a pointer to function rather than a reference to function, then it would be. This is because a lambda with no capture is implicitly convertible to the equivalent function pointer type. This conversion is allowed outside of the context of deduction.

事实并非如此。但是,如果参数是指向函数的指针而不是对函数的引用,那么它就是。这是因为没有捕获的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

This is the same reason why this fails:

这与失败的原因相同:

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

But this succeeds:

但这成功了:

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

The preferred approach would be to deduce the type of the callable and pick out the result using std::result_of:

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

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

Now you can pass your lambda, or function, or function object just fine.

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

#1


13  

A lambda function isn't a normal function. Each lambda has its own type that is not To (&)(const From&) in any case.
A non capturing lambda can decay to To (*)(const From&) in your case using:

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

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

As noted in the comments, the best you can do to get it out from a lambda is this:

正如评论中所指出的,从lambda中获取它的最佳方法是:

#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() {}

Note: I assumed that deducing return type and types of the arguments is mandatory for the real problem. Otherwise you can easily deduce the whole lambda as a generic callable object and use it directly, no need to decay anything.

注意:我认为推断返回类型和参数类型对于实际问题是必需的。否则,您可以轻松地将整个lambda推断为通用可调用对象并直接使用它,无需任何衰减。

#2


10  

If you don't need to use the deduced To type, you can just deduce the type of the whole parameter:

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

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

#3


6  

Correct me if I am wrong, but template parameters deduction deduces only exact types without considering possible conversions.

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

As a result the compiler cannot deduce To and From for To (&)(const From&) because qux expects a reference to function, but you provide a lambda which has its own type.

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

#4


2  

You have left absolutely no chance to compiler to guess what is To. Thus, you need to specify it explicitly.

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

Also, lambda here needs to be passed by pointer.

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

Finally, this version compiles ok:

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

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; });
}

#5


2  

You're expecting both implicit type conversions (from unnamed function object type to function reference type) and template type deduction to happen. However, you can't have both, as you need to know the target type to find the suitable conversion sequence.

您期望发生隐式类型转换(从未命名的函数对象类型到函数引用类型)和模板类型推导。但是,您不能同时拥有这两者,因为您需要知道目标类型以找到合适的转换序列。

#6


1  

But it doesn't explain why it couldn't match the parameter.

但它并不能解释为什么它与参数不匹配。

Template deduction tries to match the types exactly. If the types cannot be deduced, deduction fails. Conversions are never considered.

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

In this expression:

在这个表达式中:

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

The type of the lambda expression is some unique, unnamed type. Whatever that type is, it is definitely not To(const From&) - so deduction fails.

lambda表达式的类型是一些唯一的,未命名的类型。无论那种类型是什么,它绝对不是To(const From&) - 所以演绎失败了。


If I make qux a non-template function, replacing From with T and To with int, it compiles.

如果我使qux成为非模板函数,将From替换为T,将To替换为int,则编译。

That is not true. However, if the argument was a pointer to function rather than a reference to function, then it would be. This is because a lambda with no capture is implicitly convertible to the equivalent function pointer type. This conversion is allowed outside of the context of deduction.

事实并非如此。但是,如果参数是指向函数的指针而不是对函数的引用,那么它就是。这是因为没有捕获的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

This is the same reason why this fails:

这与失败的原因相同:

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

But this succeeds:

但这成功了:

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

The preferred approach would be to deduce the type of the callable and pick out the result using std::result_of:

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

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

Now you can pass your lambda, or function, or function object just fine.

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