std :: function无法区分重载函数

MS *_*nth 33 c++ overload-resolution c++11 std-function

我试图理解为什么std::function无法区分重载函数.

#include <functional>

void add(int,int){}

class A {};

void add (A, A){}

int main(){
        std::function <void(int, int)> func = add;
}
Run Code Online (Sandbox Code Playgroud)

在上面显示的代码中,function<void(int, int)>只能匹配其中一个功能,但它会失败.为什么会这样?我知道我可以通过使用lambda或函数指针到实际函数然后将函数指针存储在函数中来解决这个问题.但为什么这会失败?关于我想要选择哪个功能的上下文不清楚吗?请帮助我理解为什么这会失败,因为我无法理解为什么在这种情况下模板匹配失败.

我得到的编译错误如下:

test.cpp:10:33: error: no viable conversion from '<overloaded function type>' to
      'std::function<void (int, int)>'
        std::function <void(int, int)> func = add;
                                       ^      ~~~
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/__functional_03:1266:31: note: 
      candidate constructor not viable: no overload of 'add' matching
      'std::__1::nullptr_t' for 1st argument
    _LIBCPP_INLINE_VISIBILITY function(nullptr_t) : __f_(0) {}
                              ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/__functional_03:1267:5: note: 
      candidate constructor not viable: no overload of 'add' matching 'const
      std::__1::function<void (int, int)> &' for 1st argument
    function(const function&);
    ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/__functional_03:1269:7: note: 
      candidate template ignored: couldn't infer template argument '_Fp'
      function(_Fp,
      ^
1 error generated.
Run Code Online (Sandbox Code Playgroud)

编辑 - 除了MSalters的回答,我在这个论坛上做了一些搜索,找到了失败的确切原因.我在这篇文章中得到了纳瓦兹的答复.

我从他的答案中粘贴了副本:

    int test(const std::string&) {
        return 0;
    }

    int test(const std::string*) {
        return 0;
    }

    typedef int (*funtype)(const std::string&);

    funtype fun = test; //no cast required now!
    std::function<int(const std::string&)> func = fun; //no cast!
Run Code Online (Sandbox Code Playgroud)

那么为什么std::function<int(const std::string&)>不能按funtype fun = test上述方式工作呢?

答案是,因为std::function可以使用任何对象初始化,因为它的构造函数是模板化的,它与您传递给的模板参数无关std::function.

Jon*_*ely 22

对我们来说很明显你打算选择哪个函数,但是编译器必须遵循C++的规则而不是使用聪明的逻辑跳跃(甚至不是那么聪明的函数,就像在这样的简单情况下!)

相关的构造函数std::function是:

template<class F> function(F f);
Run Code Online (Sandbox Code Playgroud)

这是一个接受任何类型的模板.

C++ 14标准确实约束了模板(因为LWG DR 2132),因此它:

除非fCallable(20.9.12.2)用于参数类型ArgTypes...和返回类型,否则不应参与重载决策R.

这意味着编译器只允许在Functor与调用签名std::function(void(int, int)在您的示例中)兼容时调用构造函数.理论上,这应该意味着这void add(A, A)不是一个可行的论据,所以"显然"你打算使用void add(int, int).

但是,编译器无法测试" fis Callable for argument types ..."约束,直到它知道其类型为止f,这意味着它需要已经消除歧义,void add(int, int)并且void add(A, A) 它可以应用允许它拒绝一个的约束之前之后消除歧义.那些功能!

所以有一个鸡和蛋的情况,遗憾的是你需要通过准确指定add你想要使用哪个重载来帮助编译器,然后编译器可以应用约束并(相当冗余地)决定它是一个可接受的参数对于构造函数.

可以想象我们可以改变C++,以便在这种情况下,所有重载函数都会针对约束进行测试(因此我们不需要知道在测试它之前要测试哪一个),如果只有一个是可行的,那么使用那个,但这不是C++的工作原理.


MSa*_*ers 17

虽然你想要的是显而易见的,但问题是std::function不能影响重载分辨率&add.如果你要初始化一个原始函数指针(void (*func)(int,int) = &add),它确实有效.这是因为函数指针初始化是一个完成重载决策的上下文.目标类型是完全已知的.但std::function几乎可以采取任何可赎回的论据.接受参数的灵活性确实意味着你不能对重载进行解决&add.多次重载add可能是合适的.

明确的演员表会起作用,即static_cast<void(*)(int, int)> (&add).

这可以包含在一个template<typename F> std::function<F> make_function(F*)允许你写的auto func = make_function<int(int,int)> (&add)

  • @ user3493289:我不希望将来改变它.注意,如果`int`可以隐式转换为`A`然后返回,那么`std :: function <int(int,int)>`也可以保存`A add(A,A)`.正是这种灵活性使得重载不仅在技术上,而且在逻辑上也是不可能的. (2认同)
  • @ user3493289:现在的C++风格建议添加`std :: function <F> function_from_ptr <F>(F*funptr)`因为你可以编写`auto f = function_from_ptr <void()(int,int)>(&add) `.`make_shared`和`make_unique`.suggests`make_function`的模式,但请参阅http://stackoverflow.com/questions/27825559/why-is-there-no-stdmake-function (2认同)

W.F*_*.F. 5

尝试:

std::function <void(int, int)> func = static_cast<void(*)(int, int)> (add);
Run Code Online (Sandbox Code Playgroud)

地址void add(A, A)void add(int, int)显然不同。当您按名称指向函数时,编译器几乎不可能知道您需要哪个函数地址。void(int, int)这里不是提示。

  • 嗨,我知道如何解决这个问题。但我的问题是“为什么首先存在歧义”?函数参数本质上不是应该有助于选择正确函数的签名的一部分吗? (2认同)