std :: function无法区分重载函数 [英] std::function fails to distinguish overloaded functions

查看:247
本文介绍了std :: function无法区分重载函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图理解为什么 std :: function 不能区分重载函数。

I am trying to understand why std::function is not able to distinguish between overloaded functions.

#include <functional>

void add(int,int){}

class A {};

void add (A, A){}

int main(){
        std::function <void(int, int)> func = add;
}

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

In the code shown above, function<void(int, int)> can match only one of these functions and yet it fails. Why is this so? I know I can work around this by using a lambda or a function pointer to the actual function and then storing the function pointer in function. But why does this fail? Isn't the context clear on which function I want to be chosen? Please help me understand why this fails as I am not able to understand why template matching fails in this case.

我在clang上遇到的编译器错误如下:

The compiler errors that I get on clang for this are as follows:

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.

编辑-除了MSalters的答案外,我还在这个论坛上进行了搜索,并找到了失败的确切原因。我从Nawaz的答复中得到了答案发布

EDIT - In addition to MSalters' answer, I did some searching on this forum and found the exact reason why this fails. I got the answer from Nawaz's reply in this post.

我已经从他的答案中粘贴了以下内容:

I have copy pasted from his answer here:

    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!

所以为什么 std :: function< int(const std :: string& )> 不能像 funtype fun = test 那样工作吗?

So why std::function<int(const std::string&)> does not work the way funtype fun = test works above?

好的答案是,因为 std :: function 可以用任何对象初始化,因为它的构造函数是模板化的,所以与传递给<$的模板参数无关c $ c> std :: function 。

Well the answer is, because std::function can be initialized with any object, as its constructor is templatized which is independent of the template argument you passed to std::function.

推荐答案

对于我们来说,显而易见的是您打算选择的函数,但是编译器必须遵循C ++的规则,不能使用巧妙的逻辑跳跃(甚至不像这样的简单情况那样巧妙)!

It's obvious to us which function you intend to be chosen, but the compiler has to follow the rules of C++ not use clever leaps of logic (or even not so clever ones, as in simple cases like this!)

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

template<class F> function(F f);

这是一个接受 any 类型的模板。

which is a template that accepts any type.

C ++ 14标准确实限制了模板(因为 LWG DR 2132 ),这样:

The C++14 standard does constrain the template (since LWG DR 2132) so that it:


除非 f ArgTypes ... 和返回类型 R ,$ c>是可调用的(20.9.12.2)。 p>

shall not participate in overload resolution unless f is Callable (20.9.12.2) for argument types ArgTypes... and return type R.

这意味着编译器仅允许在 Functor 被调用时调用构造函数。与 std :: function 的呼叫签名兼容(在您的示例中为 void(int,int)) 。从理论上讲,这应该意味着 void add(A,A)不是可行的参数,因此显然您打算使用 void add(int ,int)

which means that the compiler will only allow the constructor to be called when Functor is compatible with the call signature of the std::function (which is void(int, int) in your example). In theory that should mean that void add(A, A) is not a viable argument and so "obviously" you intended to use void add(int, int).

但是,编译器无法测试 f 可用于参数类型...约束,直到知道 f 的类型为止,这意味着它需要在<$ c之间消除歧义$ c> void add(int,int)和 void add(A,A) 之前,它可以应用

However, the compiler can't test the "f is Callable for argument types ..." constraint until it knows the type of f, which means it needs to have already disambiguated between void add(int, int) and void add(A, A) before it can apply the constraint that would allow it to reject one of those functions!

因此存在鸡与蛋的情况,不幸的是,这意味着您需要通过准确指定哪个来帮助编译器要使用的 add 的重载,然后 then 编译器可以应用该约束,并(而不是多余地)确定它是

So there's a chicken and egg situation, which unfortunately means that you need to help the compiler out by specifying exactly which overload of add you want to use, and then the compiler can apply the constraint and (rather redundantly) decide that it is an acceptable argument for the constructor.

可以想象,我们可以更改C ++,以便在这种情况下 all 针对约束函数测试了约束函数(因此我们不需要在测试之前就知道要测试哪个函数),并且如果只有一个可行,则可以使用该函数,但这不是C ++的工作原理。

It is conceivable that we could change C++ so that in cases like this all the overloaded functions are tested against the constraint (so we don't need to know which one to test before testing it) and if only one is viable then use that one, but that's not how C++ works.

这篇关于std :: function无法区分重载函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆