重载 std::function<...>
Posted
技术标签:
【中文标题】重载 std::function<...>【英文标题】:Overloading on std::function<...> 【发布时间】:2011-06-11 13:02:18 【问题描述】:给定以下代码:-
#include <algorithm>
#include <iostream>
#include <functional>
#include <string>
void func(std::function<void(void)> param)
param();
void func(std::function<void(int)> param)
param(5);
int main(int argc, char* argv[])
func([] () std::cout << "void(void)" << std::endl; );
func([] (int i) std::cout << "void(int): " << i << std::endl; );
std::string line;
std::getline(std::cin, line);
return 0;
来自 VS2010 的编译错误:-
CppTest.cpp(18): error C2668: 'func' : ambiguous call to overloaded function
1> CppTest.cpp(11): could be 'void func(std::tr1::function<_Fty>)'
1> with
1> [
1> _Fty=void (int)
1> ]
1> CppTest.cpp(6): or 'void func(std::tr1::function<_Fty>)'
1> with
1> [
1> _Fty=void (void)
1> ]
1> while trying to match the argument list '(`anonymous-namespace'::<lambda0>)'
1>CppTest.cpp(19): error C2668: 'func' : ambiguous call to overloaded function
1> CppTest.cpp(11): could be 'void func(std::tr1::function<_Fty>)'
1> with
1> [
1> _Fty=void (int)
1> ]
1> CppTest.cpp(6): or 'void func(std::tr1::function<_Fty>)'
1> with
1> [
1> _Fty=void (void)
1> ]
1> while trying to match the argument list '(`anonymous-namespace'::<lambda1>)'
从 g++-4.5 编译错误
program2.cpp: In function ‘int main(int, char**)’:
program2.cpp:18:68: error: call of overloaded ‘func(main(int, char**)::<lambda()>)’ is ambiguous
program2.cpp:6:10: note: candidates are: void func(std::function<void()>)
program2.cpp:11:10: note: void func(std::function<void(int)>)
program2.cpp:19:79: error: call of overloaded ‘func(main(int, char**)::<lambda(int)>)’ is ambiguous
program2.cpp:6:10: note: candidates are: void func(std::function<void()>)
program2.cpp:11:10: note: void func(std::function<void(int)>)
所以编译器似乎无法弄清楚 lambda [] () -> void 只能分配给 std::function
【问题讨论】:
【参考方案1】:这是应该发生的还是只是编译器的缺陷?
这应该会发生。 std::function
有一个构造函数模板,可以接受任何类型的参数。直到选择并实例化构造函数模板后,编译器才能知道它将遇到错误,并且它必须能够选择函数的重载才能执行此操作。
最直接的解决方法是使用强制转换或显式构造正确类型的 std::function
对象:
func(std::function<void()>([]()));
func(std::function<void(int)>([](int)));
如果您的编译器支持无捕获 lambda 到函数指针的转换,并且您的 lambda 没有捕获任何内容,则可以使用原始函数指针:
void func(void (*param)())
void func(void (*param)(int))
(您使用的似乎是 Visual C++ 2010,它不支持此转换。直到 Visual Studio 2010 发布之前,该转换才添加到规范中,添加它为时已晚。)
为了更详细地解释这个问题,请考虑以下几点:
template <typename T>
struct function
template <typename U>
function(U f)
;
这基本上就是所讨论的std::function
构造函数的样子:您可以使用任何参数调用它,即使该参数没有意义并且会在其他地方导致错误。例如,function<int()> f(42);
将使用U = int
调用此构造函数模板。
在您的具体示例中,编译器在重载解析期间找到两个候选函数:
void func(std::function<void(void)>)
void func(std::function<void(int)>)
参数类型,一些我们将称为F
的无法表达的 lambda 类型名称,与其中任何一个都不完全匹配,因此编译器开始查看它可以对F
进行哪些转换以尝试进行它匹配这些候选函数之一。在寻找转换时,它会找到前面提到的构造函数模板。
此时编译器所看到的只是它可以调用任何一个函数,因为
它可以将F
转换为std::function<void(void)>
,使用带有U = F
和的转换构造函数
它可以使用带有U = F
的转换构造函数将F
转换为std::function<void(int)>
。
在您的示例中,很明显只有其中一个会成功而不会出错,但在一般情况下,情况并非如此。编译器不能做任何进一步的事情。它必须报告歧义并失败。它不能选择一个,因为两种转换都同样好,而且没有一个过载比另一个更好。
【讨论】:
如果我做对了,请告诉我。这个sn-p同样的原因也有同样的问题(不能及时实例化Struct来判断func重载)?template <typename T> struct Struct Struct(T var) ; void func2(Struct<int> obj) void func2(Struct<double> obj) int main() func2(2.5);
不,这有点不同。该错误是因为T
处于非推断上下文中。我将在我的答案中编辑解释;这个评论不够大。
在 C++ 标准草案的许多领域,如果某些条件不满足,委员会要求将一些构造函数/函数模板排除在重载决议之外。这通常可以通过“SFINAE”来实现。 (参见例如 shared_ptr 的模板化构造函数)。我想知道为什么他们没有在这里用 std::function 做类似的事情。
@sellibitze:我认为这可能会很困难,因为std::function
可以存储许多不同类型的类型。即便如此,在 OP 的情况下,仍然可能存在歧义,例如如果您传递了struct S void operator()() void operator()(int) ;
类型的对象。以上是关于重载 std::function<...>的主要内容,如果未能解决你的问题,请参考以下文章
没有可行的重载'='用于将std :: function回调赋值为成员函数
使用 + (unary plus) 为 lambda 解决函数指针和 std::function 上的模棱两可的重载