是不是模板参数(签名)的std :: function部分它的类型? [英] Isn't the template argument (the signature) of std::function part of its type?

查看:201
本文介绍了是不是模板参数(签名)的std :: function部分它的类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

给定以下代码,什么是歧义的背后的原因?我可以规避它,还是我必须保持(烦人的)显式转换?

  #include< functional& 

using namespace std;

int a(const function< int()& f)
{
return f();
}

int a(const function< int(int)>& f)
{
return f(0)
}

int x(){return 22; }

int y(int){return 44; }

int main()
{
a(x); //调用是不明确的。
a(y); //调用是不明确的。

a((function< int()>)x); //工作。
a((function< int(int)>)y); //工作。

return 0;有趣的是,如果我注释掉 a(),那么我们可以使用<$ c $ b>


$ b <参数并调用 a(x)函数在我的主要,编译正确失败,因为 x 和参数函数只有 a()函数可用。如果编译器在这种情况下失败,为什么当两个 a()函数存在时会出现歧义?



我试过VS2010和g ++ v。4.5。问题是 function< int()>

; 函数< int(int)> 可以从相同的函数构造。这是VS2010中的 std :: function 的构造函数声明:

  template< class _Fx> 
function(_Fx _Func,typename _Not_integral<!_ Is_integral< _Fx> :: value,int> :: _ Type = 0);

忽略SFINAE部分,几乎可以构建任何东西。

std :: / boost :: function 使用一种称为类型擦除的技术,允许任意对象/函数传入,只要它们满足签名。这样做的一个缺点是,当提供一个不能被调用的对象,而不是在构造函数中时,在实现的最深部分(在保存的函数被调用时)会出现一个错误。






问题可以用这个小类来说明:

  template< class Signature> 
class myfunc {
public:
template< class Func>
myfunc(Func a_func){
// ...
}
};

现在,当编译器搜索重载集合的有效函数时,它会尝试转换参数if不存在完美拟合函数。转换可以通过函数参数的构造函数,或者通过赋给函数的参数的转换操作符来实现。在我们的例子中,它是前者。

编译器尝试第一个重载 a 。为了使其可行,它需要进行转换。要将 int(*)()转换为 myfunc< int()> myfunc 。作为一个需要任何东西的模板,转换自然就会成功。

现在它尝试与第二个重载相同。构造函数仍然是相同的,仍然采取任何给它,转换也工作。

在剩余的2个函数在重载集合,编译器是一个悲伤的熊猫,不知道该怎么办






所以最后, Signature 模板的一部分在声明/定义时属于类型,但是当你想构造一个对象时不会。






编辑

在回答标题问题时,我完全忘记了第二个问题。 :


我可以规避它,还是我必须保持(烦人的)显式转换?




Afaik,您有3个选项。





  • 创建适当类型的函数对象,并传递



    code> function< int()> fx = x;
    function< int(int)> fy = y;
    a(fx);
    a(fy) code>


  • 在函数中隐藏繁琐的转换并使用TMP获取正确的签名




TMP(模板元编程)版本非常详细,并带有样板代码,但它隐藏了来自客户端的转换。可以找到一个示例版本在这里,它依赖于部分专用于函数指针类型的 get_signature 元功能(并提供了一个很好的例子如何模式匹配可以在C ++中工作):

  template< class F& 
struct get_signature;

template< class R>
struct get_signature< R(*)()> {
typedef R type();
};

template< class R,class A1>
struct get_signature< R(*)(A1)> {
typedef R type(A1);
};

当然,这需要扩展你要支持的参数数量,做一次然后埋在get_signature.h头。 :)



另一个我认为但立即丢弃的选项是SFINAE,它会引入比TMP版本更多的样板代码。



所以,是的,这是我知道的选择。希望其中一个为你工作。 :)


Given the following code, what is the reason behind the ambiguity? Can I circumvent it or will I have to keep the (annoying) explicit casts?

#include <functional>

using namespace std;

int a(const function<int ()>& f)
{
    return f();
}

int a(const function<int (int)>& f)
{
    return f(0);
}

int x() { return 22; }

int y(int) { return 44; }

int main()
{
    a(x);  // Call is ambiguous.
    a(y);  // Call is ambiguous.

    a((function<int ()>)x);    // Works.
    a((function<int (int)>)y); // Works.

    return 0;
}

Interestingly, if I comment out the a() function with the function<int ()> parameter and call a(x) in my main, the compilation correctly fails because of the type mismatch between x and the argument function<int (int)> of the only a() function available. If the compiler fails in that case, why would there be any ambiguity when the two a() functions are present?

I've tried with VS2010 and g++ v. 4.5. Both give me the exact same ambiguity.

解决方案

The problem is that both function<int()> and function<int(int)> are constructible from the same function. This is what the constructor declaration of std::function looks like in VS2010:

template<class _Fx>
function(_Fx _Func, typename _Not_integral<!_Is_integral<_Fx>::value, int>::_Type = 0);

Ignoring the SFINAE part, it is constructible from pretty much anything.
std::/boost::function employ a technique called type erasure, to allow arbitary objects/functions to be passed in, so long they satisfy the signature when being called. One drawback from that is, that you get an error in the deepest part of the implementation (where the saved function is being called) when supplying an object which can't be called like the signature wants it to, instead of in the constructor.


The problem can be illustrated with this little class:

template<class Signature>
class myfunc{
public:
    template<class Func>
    myfunc(Func a_func){
        // ...
    }
};

Now, when the compiler searches for valid functions for the overload set, it tries to convert the arguments if no perfect fitting function exists. The conversion can happen through the constructor of the parameter of the function, or through a conversion operator of the argument given to the function. In our case, it's the former.
The compiler tries the first overload of a. To make it viable, it needs to make a conversion. To convert a int(*)() to a myfunc<int()>, it tries the constructor of myfunc. Being a template that takes anything, the conversion naturally succeeds.
Now it tries the same with the second overload. The constructor still being the same and still taking anything given to it, the conversion works too.
Being left with 2 functions in the overload set, the compiler is a sad panda and doesn't know what to do, so it simply says the call is ambigious.


So in the end, the Signature part of the template does belong to the type when making declarations/definitions, but doesn't when you want to construct an object.


Edit:
With all my attention on answering the title-question, I totally forgot about your second question. :(

Can I circumvent it or will I have to keep the (annoying) explicit casts?

Afaik, you have 3 options.

  • Keep the cast
  • Make a function object of the appropriate type and pass that

    function<int()> fx = x; function<int(int)> fy = y; a(fx); a(fy);

  • Hide the tedious casting in a function and use TMP to get the right signature

The TMP (template metaprogramming) version is quite verbose and with boilerplate code, but it hides the casting from the client. An example version can be found here, which relies on the get_signature metafunction that is partially specialized on function pointer types (and provides a nice example how pattern matching can work in C++):

template<class F>
struct get_signature;

template<class R>
struct get_signature<R(*)()>{
  typedef R type();
};

template<class R, class A1>
struct get_signature<R(*)(A1)>{
  typedef R type(A1);
};

Of course, this needs to be extended for the number of arguments you want to support, but that is done once and then buried in a "get_signature.h" header. :)

Another option I consider but immediatly discarded was SFINAE, which would introduce even more boilerplate code than the TMP version.

So, yeah, that are the options that I know of. Hope one of them works for you. :)

这篇关于是不是模板参数(签名)的std :: function部分它的类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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