Passing rvalues through std::bind
The reason this fails is because when you specify foo<Movable>
, the function you're binding to is:
void foo(Movable&&) // *must* be an rvalue
{
}
However, the value passed by std::bind
will not be an rvalue, but an lvalue (stored as a member somewhere in the resulting bind
functor). That, is the generated functor is akin to:
struct your_bind
{
your_bind(Movable arg0) :
arg0(arg0)
{}
void operator()()
{
foo<int>(arg0); // lvalue!
}
Movable arg0;
};
Constructed as your_bind(Movable())
. So you can see this fails because Movable&&
cannot bind to Movable
.†
A simple solution might be this instead:
auto f = std::bind(foo<Movable&>, Movable());
Because now the function you're calling is:
void foo(Movable& /* conceptually, this was Movable& &&
and collapsed to Movable& */)
{
}
And the call works fine (and, of course, you could make that foo<const Movable&>
if desired). But an interesting question is if we can get your original bind to work, and we can via:
auto f = std::bind(foo<Movable>,
std::bind(static_cast<Movable&&(&)(Movable&)>(std::move<Movable&>),
Movable()));
That is, we just std::move
the argument before we make the call, so it can bind. But yikes, that's ugly. The cast is required because std::move
is an overloaded function, so we have to specify which overload we want by casting to the desired type, eliminating the other options.
It actually wouldn't be so bad if std::move
wasn't overloaded, as if we had something like:
Movable&& my_special_move(Movable& x)
{
return std::move(x);
}
auto f = std::bind(foo<Movable>, std::bind(my_special_move, Movable()));
Which is much simpler. But unless you have such a function laying around, I think it's clear you probably just want to specify a more explicit template argument.
† This is different than calling the function without an explicit template argument, because explicitly specifying it removes the possibility for it to be deduced. (T&&
, where T
is a template parameter, can be deduced to anything, if you let it be.)
std::bind and rvalue reference
It might become clearer if you write down what std::bind
schematically does.
// C++14, you'll have to write a lot of boilerplate code for C++11
template <typename FuncT, typename ArgT>
auto
bind(FuncT&& func, ArgT&& arg)
{
return
[
f = std::forward<FuncT>(func),
a = std::forward<ArgT>(arg)
]() mutable { return f(a); }; // NB: a is an lvalue here
}
Since you can call the function object std::bind
gives you multiple times, it cannot “use up” the captured argument so it will be passed as an lvalue reference. The fact that you pass bind
itself an rvalue only means that there is no copy made on the line where a
is initialized.
If you try to compile your example with the schematic bind
shown above, you'll also get a more helpful error message from your compiler.
main.cxx: In instantiation of ‘bind(FuncT&&, ArgT&&)::<lambda()> mutable [with FuncT = main()::<lambda(Widget&&)>; ArgT = Widget]’:
main.cxx:10:33: required from ‘struct bind(FuncT&&, ArgT&&) [with FuncT = main()::<lambda(Widget&&)>; ArgT = Widget]::<lambda()>’
main.cxx:11:31: required from ‘auto bind(FuncT&&, ArgT&&) [with FuncT = main()::<lambda(Widget&&)>; ArgT = Widget]’
main.cxx:18:59: required from here
main.cxx:11:26: error: no match for call to ‘(main()::<lambda(Widget&&)>) (Widget&)’
]() mutable { return f(a); }; // NB: a is an lvalue here
^
main.cxx:11:26: note: candidate: void (*)(Widget&&) <conversion>
main.cxx:11:26: note: conversion of argument 2 would be ill-formed:
main.cxx:11:26: error: cannot bind ‘Widget’ lvalue to ‘Widget&&’
main.cxx:18:33: note: candidate: main()::<lambda(Widget&&)> <near match>
auto lambda = bind([](Widget&&){ return; }, std::move(w));
^
main.cxx:18:33: note: conversion of argument 1 would be ill-formed:
main.cxx:11:26: error: cannot bind ‘Widget’ lvalue to ‘Widget&&’
]() mutable { return f(a); }; // NB: a is an lvalue here
passing rvalue raises cannot bind to lvalue
f(t2, t1);
t2
has a name, so it is an lvalue. It's type is rvalue, but in an expression it's type is an lvalue. In order to pass it as an rvalue reference, you need to use std::forward
(move
or casting would be inappropriate here, because T1 and T2 are actually universal references, not rvalue references, see edit).
#include <iostream>
using namespace std;
template <typename F, typename T1, typename T2>
void flip2(F f, T1 &&t1, T2 &&t2)
{
f(std::forward<T2>(t2), std::forward<T1>(t1));
}
void g(int &&i, int &j)
{
cout << i << " " << j << endl;
}
int main(void)
{
int i = 1;
flip2(g, i, 42);
}
http://ideone.com/Aop2aJ
Why
Consider:
template<typename T>
void printAndLog(T&& text) {
print(text);
log(text);
}
int main() {
printAndLog(std::string("hello, world!\n"));
}
When you use a variable's name, the expression-type is lvalue (glvalue?); the rvalueness is discarded. Otherwise in the example above, we'd have lost text
to print()
. Instead, we have to be explicit when we want to our rvalue to behave like one:
template<typename T>
void printAndLog(T&& text) {
print(text);
log(std::forward<T>(text)); // if text is an rvalue, give it up.
}
Edit
I used std::forward
because T1&&
and T2&&
are universal references, not rvalue references. https://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers
std::bind with a movable paramter
Your troubles come from std::function
.
std::bind
returns some callable which has Param
as member variable. When std::function
instance is created all members of that functor generated by bind
are stored by copying. When copy constructor of Param
is commented out, it is just not possible to create std::function
wrapper. And this is your issue.
As solution, get rid of std::function
, and just pass any callable as template parameter:
template<class F>
void Do(F&& callback)
{
callback(5);
}
Live demo
std::bind(): bind lambda with rvalue reference as argument
The specification for std::bind
is rather dense. In brief, a plain bound argument (not a bind expression, not a reference_wrapper
, and not a placeholder) is passed to the bound function as std::forward<Vi>(tid)
where Vi
is TiD cv &
, cv
is the cv-qualifiers of the call wrapper, TiD
is the type decay_t<Ti>
, Ti
is the type actually passed to bind
, and tid
is "an lvalue of type TiD
constructed from std::forward<Ti>(ti)
", and ti
is the argument passed to bind
.
Applying this to your call, we see that Ti
is Dog
and ti
is Dog("DogABC")
. So TiD
is also Dog
, and Vi
is cv Dog &
, which means that std::forward<Vi>(Tid)
is an lvalue, and the compiler complains because your lambda takes an rvalue reference parameter, and an rvalue reference parameter cannot bind to a lvalue.
Can't bind lvalue to rvalue in member function but ok in global function
It's because the automatic template deduction for globalDoSomething
infers T
as int&
.
If you explicitly instantiate the template function with globalDoSomething<int>(b);
like you did for the member function of the template class, it will also fail to compile.
Conversely, if you instantiate the template class with A<int&> a;
, it will successfully compile.
Related Topics
How to Check If the Input Is a Valid Integer Without Any Other Chars
Force All Classes to Implement/Override a 'Pure Virtual' Method in Multi-Level Inheritance Hierarchy
Division by Zero: Undefined Behavior or Implementation Defined in C And/Or C++
Modular Exponentiation for High Numbers in C++
How to Validate Input Using Scanf
5 Years Later, Is There Something Better Than the "Fastest Possible C++ Delegates"
How to Make a Multiple-Read/Single-Write Lock from More Basic Synchronization Primitives
Programmatically Reading a Web Page
When Should You Use the "This" Keyword in C++
How to Identify the File Content as Ascii or Binary
What Is Dynamic Initialization of Object in C++