重载 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 和 lambda [] (int) -> 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&lt;int()&gt; f(42); 将使用U = int 调用此构造函数模板。

在您的具体示例中,编译器在重载解析期间找到两个候选函数:

void func(std::function<void(void)>)
void func(std::function<void(int)>)

参数类型,一些我们将称为F 的无法表达的 lambda 类型名称,与其中任何一个都不完全匹配,因此编译器开始查看它可以对F 进行哪些转换以尝试进行它匹配这些候选函数之一。在寻找转换时,它会找到前面提到的构造函数模板。

此时编译器所看到的只是它可以调用任何一个函数,因为

它可以将F 转换为std::function&lt;void(void)&gt;,使用带有U = F 和的转换构造函数 它可以使用带有U = F的转换构造函数将F转换为std::function&lt;void(int)&gt;

在您的示例中,很明显只有其中一个会成功而不会出错,但在一般情况下,情况并非如此。编译器不能做任何进一步的事情。它必须报告歧义并失败。它不能选择一个,因为两种转换都同样好,而且没有一个过载比另一个更好。

【讨论】:

如果我做对了,请告诉我。这个sn-p同样的原因也有同样的问题(不能及时实例化Struct来判断func重载)? template &lt;typename T&gt; struct Struct Struct(T var) ; void func2(Struct&lt;int&gt; obj) void func2(Struct&lt;double&gt; 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 上的模棱两可的重载

带有 std::function 参数的重载运算符

模板类重载 std::bind 成员函数

C++11 std::bind和std::function解析

如何在 std::function 的参数中进行可变参数推导?