【问题标题】:Overloading on std::function<...>重载 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。这应该发生还是只是编译器的缺陷?

【问题讨论】:

    标签: templates lambda c++11 overloading


    【解决方案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) { } }; 类型的对象。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-04
    • 1970-01-01
    相关资源
    最近更新 更多