Make callback temporary in VS 2010

I have a callback implementation using rvalue references to store arguments that work fine with gcc but don't compile in VS 2010 on some code. 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; } 

With gcc 4.4 this gives:

$ g ++ clbck.cpp -std = C ++ 0x -o clbck && & &. / clbck
Sample1 function here
Sample2 function here
Sample2 function here

However, it does not compile in VS 2010 when trying to create an instance of registerFunc due to the marked line:

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 lvalue to rvalue reference

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

And while you're at it, is there something wrong with how I handle these callbacks? It works great with function pointers and functors (I have yet to check lambdas), the only drawback I found is the one described above. (Keep in mind that the code given here is just a small demonstration, in the actual code I do not give users any pointers, I actually use this to execute functions in different threads in the Qt application).

+1
source share
3 answers

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

The problem you are facing is that she is not allowed to initialize the rvalue reference with the lvalue of the target type. But you are trying to do this because the constructor parameter "arg" is the named parameter rvalue, which makes this expression lvalue in the list of initializers. You probably used an old version of GCC to compile this code. Newer versions will also complain about this.

You can save some problems 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); 

If you really want to deal with parameter binding, 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; }; 

Keep in mind that the type of the parameter T && where T can be inferred is of particular importance. This is the catch-all parameter. The output of the template argument will make T a reference to the lvalue (and T & &, as well as the standard discard rules) if the argument was an lvalue. So, if you always want the parameter to be stored as a copy, you need 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)); } 

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

 registerFunc(some_func,std::cref(some_obj)); 
+1
source

I believe that Visual Studio has the right to complain, and you can find an explanation here .

In registerFunc , the arg expression is an lvalue (it has a name). If you want to forward it as a rvalue reference, you should use std::forward :

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

The same problem occurs in the FuncType constructor, but adding std::forward gives an interesting warning:

Link item

initialized temporary, which is not saved after exiting the constructor

Unfortunately, I don’t have enough rvalue references to help you later, but I doubt that the rvalue reference element makes sense: rvalue references are tied to objects that are about to die, will it make sense to store this?

+2
source

Why not just use lambda expressions?

 class B { virtual void execute() = 0; }; template<typename T> class F : public B { T t; public: F(T&& arg) : t(std::forward<T>(arg)) {} void execute() { return t(); } }; template<typename T> F<T> make_f(T&& t) { return F<T>(std::forward<T>(t)); } int main() { std::string var; B* callback = make_f([=]() { std::cout << var << std::endl; }); } 

Or, indeed, std::function<void()> , for which it is needed.

0
source

Source: https://habr.com/ru/post/1332683/


All Articles