使用lambda函数签名的函数重载

时间:2015-01-03 00:12:28

标签: c++ c++11 lambda overloading

考虑以下示例

void foo(const std::function<int()>& f) {
    std::cout << f() << std::endl;
}

void foo(const std::function<int(int x)>& f) {
std::cout << f(5) << std::endl;
}

int main() {
    foo([](){return 3;});

    foo([](int x){return x;});
}

这不会编译,因为对foo的调用被认为是不明确的。据我所知,这是因为lambda函数不是先验的std::function,而是必须转换为它,并且有一个std::function构造函数接受任意参数。

也许有人可以向我解释为什么有人会创建一个带有任意参数的隐式构造函数。然而,我的实际问题是是否存在一种解决方法,它允许使用lambda函数的函数签名来重载函数foo。我已经尝试过函数指针,但这不起作用,因为捕获lambda函数不能转换为普通的函数指针。

非常欢迎任何帮助。

2 个答案:

答案 0 :(得分:15)

根据C ++ 11,您的编译器是正确的。在C ++ 14中,添加了一条规则,指出构造函数模板不应参与重载解析,除非参数的类型实际上可以用std::function的参数类型调用。因此,这段代码应该用C ++ 14编译,但不能用C ++ 11编译。认为这是对C ++ 11的疏忽。

目前,您可以通过显式转换解决此问题:

foo(std::function<int()>([](){return 3;}));

答案 1 :(得分:6)

http://coliru.stacked-crooked.com/a/26bd4c7e9b88bbd0

使用std :: function的另一种方法是使用模板。模板避免了与std :: function相关的内存分配开销。模板类型扣除机制将推导出传递的lambda的正确类型,以便呼叫站点转换消失。但是你仍然有消除no-args vs args情况下的重载的歧义。

您可以使用具有与enable_if类似的尾随返回类型的技巧来执行此操作。

template<typename Callable>
auto baz(Callable c) 
    -> decltype(c(5), void())
{
    std::cout << c(5) << std::endl;
}

当模板参数Callable可以使用参数5调用时,上面的baz重载只是一个有效的重载候选。

你可以在此基础上加入更多高级机制,使其更通用(即可变参数包的args扩展为可调用),但我想展示基本机制的工作原理。