在VS 2010上使回调接受临时 [英] Make callback accept temporary on VS 2010

查看:180
本文介绍了在VS 2010上使回调接受临时的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个回调实现使用右值引用存储参数,它与gcc工作正常,但无法在VS 2010中编译一些代码。简短版本:

I have a callback implementation using rvalue references to store arguments that works fine with gcc, but fails to compile in VS 2010 on some code. A short version:

#include <iostream>
#include <string>

class B {
public:
    virtual void execute() = 0;
};

template<typename FuncType, typename ArgType>
class F : public B {
public:
    F(FuncType func, ArgType && arg) : f(func), arg(arg) {}
    void execute() { f(arg); }
private:
    FuncType f;
    ArgType && arg;
};

template<typename FuncType, typename ArgType>
B * registerFunc(FuncType func, ArgType && arg)
{
    return new F<FuncType, ArgType>(func, arg);
}

void myFunction(std::string text)
{
    std::cout << "Function " << text << " here" << std::endl;
}

int main()
{
    const char text1[] = "sample1";
    std::string text2("sample2");

    B * b = registerFunc(myFunction, text1);
    b->execute();
    delete b;

    b = registerFunc(myFunction, text2);
    b->execute();
    delete b;

    // VS 2010 fails because of this call
    b = registerFunc(myFunction, text2.c_str());
    b->execute();
    delete b;

    return 0;
}

使用gcc 4.4,会生成:

With gcc 4.4 this produces:


$ g ++ clbck.cpp -std = c ++ 0x -o clbck&& ./clbck

此处的函数sample1

此处的函数sample2

此处的函数sample2

$ g++ clbck.cpp -std=c++0x -o clbck && ./clbck
Function sample1 here
Function sample2 here
Function sample2 here

但是,由于标记行,试图实例化registerFunc时,它无法在VS 2010中编译:

However it fails to compile in VS 2010 when trying to instantiate registerFunc because of the marked line:


错误C2664: 'F :: F(FuncType,ArgType&&)':无法将参数2从'const char *'转换为'const char *&&'

with

[

FuncType = void(__cdecl *)(std :: string),

ArgType = const char *

]

您不能将左值绑定到右值引用

error C2664: 'F::F(FuncType,ArgType &&)' : cannot convert parameter 2 from 'const char *' to 'const char *&&'
with
[
FuncType=void (__cdecl *)(std::string),
ArgType=const char *
]
You cannot bind an lvalue to an rvalue reference

Googling在VS2010上发现了与Boost 1.44类似的错误,但推荐的解决方案使用右值引用。是否真的没有其他方法?

Googling discovered a similar error with Boost 1.44 on VS2010, but the recommended solution there is not to use rvalue references at all. Is there really no other way?

当你在这里,有什么问题,我处理这些回调的方式?它工作正常与函数指针和函子(我还没有测试lambdas),唯一的缺点,我发现是上述的一个。 (请记住这里显示的代码只是一个小的演示,在实际代码中我不给用户任何指针;我实际上使用这个在Qt应用程序中的不同线程中执行函数)。

And while you're at it, is there something wrong with the way I'm handling these callbacks? It works fine with function pointers and functors (I have yet to test lambdas), the only downside I found is the one described above. (Bear in mind the code shown here is just a small demonstration, in the actual code I don't give user any pointers; I'm actually using this to execute functions in different threads in a Qt application).

推荐答案

关于右值引用成员,你希望实现什么并不完全清楚。它只是看起来不对。你应该避免存储引用。如果你想让函数对象存储一个类似引用的对象(很像std :: bind的行为),你仍然可以使用std :: ref和std :: cref。

It's not exactly clear what you hope to achieve with respect to the rvalue reference member. It just doesn't look right. You should avoid storing a reference. You can still use things like std::ref and std::cref if you want the function object to store a reference-like object (much like std::bind behaves).

您遇到的问题是,不允许使用目标类型的左值初始化右值引用。但是尝试这样做是因为构造函数的参数arg是一个名为的值,它使得它在初始化器列表中是一个左值表达式。您可能使用旧的GCC版本来编译此代码。

The issue you ran into is that it's not allowed to initialize an rvalue reference with an lvalue of the target type. But you try to do this because the constructor's parameter "arg" is a named rvalue reference which makes it an lvalue expression in the initializer list. You probably used an old GCC version to compile this code. Newer versions will also complain about this.

您可以通过依靠std :: bind来节省自己一些麻烦:

You can save yourself some trouble by relying on std::bind:

template<class FuncType>
class F : public B {
public:
  explicit F(FuncType func)
  : f(std::forward<FuncType>(func))
  {}
  void execute() { f(); }
private:
  FuncType f;
};

....

auto fun = std::bind(myFunction,text2.c_str());
B* ptr = new F<decltype(fun)>(fun);

如果你真的想自己处理参数绑定,你应该这样做: p>

If you really want to deal with the parameter binding yourself, you should do it like this:

template<class FuncType, class ParamType>
class F : public B {
public:
  F(FuncType func, ParamType para)
  : f(std::forward<FuncType>(func))
  , p(std::forward<ParamType>(para))
  void execute() { f(p); }
private:
  FuncType f;
  ParamType p;
};

请记住,参数类型T&其中T可以推导出具有特殊意义。这是一个catch-all参数。在参数是左值的情况下,模板参数扣除将使T成为引用值(和参考折叠规则的T&&& amp;&因此,如果你总是希望参数存储为一个副本,你必须写

Keep in mind that the parameter type T&& where T can be deduced has a special meaning. It's a "catch-all" parameter. Template argument deduction will make T an lvalue reference (and T&& as well by the reference collapsing rules) in case the argument was an lvalue. So, if you always want the parameter to be stored as a copy you have to write

template<class FuncType, class ArgType>
B * registerFunc(FuncType func, ArgType && arg)
{
  typedef typename std::decay<ArgType>::type datype;
  return new F<FuncType,datype>(func, std::forward<ArgType>(arg));
}

我喜欢这样的registerFunc函数。它默认复制函数对象和参数对象。覆盖这可以通过std :: ref和std :: cref:

I would prefer a registerFunc function like this. It copies the function object and parameter object by default. Overriding this can be done via std::ref and std::cref:

registerFunc(some_func,std::cref(some_obj));

这篇关于在VS 2010上使回调接受临时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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