Here is the source code that can be used to reproduce the issue:
#include <iostream>
#include <boost/bind.hpp>
#include <boost/move/move.hpp>
#include <boost/ref.hpp>
std::ostream& dump_to_stream(std::ostream& os, int a, int b) {
return os << a << '\n' << b << '\n';
}
template <class R, class F>
R call_under_lock(F f) {
// lock();
R r = f();
// unlock();
return boost::move(r);
}
int main() {
std::ostream& os = call_under_lock<std::ostream&>(
boost::bind(&dump_to_stream, boost::ref(std::cout), 1, 2));
}
When using C++03 mode in GCC the code compiles without any problems. On the other hand the use of C++0x mode produces the following error:
$ g++ -I../../boost_latest -std=c++0x -O2 -Wall -Wextra -c -o test.o test.cpp
(...)
test.cpp: In function ‘R call_under_lock(F) [with R = std::basic_ostream<char>&, F = boost::_bi::bind_t<std::basic_ostream<char>&, std::basic_ostream<char>& (*)(std::basic_ostream<char>&, int, int), boost::_bi::list3<boost::reference_wrapper<std::basic_ostream<char> >, boost::_bi::value<int>, boost::_bi::value<int> > >]’:
test.cpp:20:62: instantiated from here
test.cpp:15:23: error: invalid initialization of non-const reference of type ‘std::basic_ostream<char>&’ from an rvalue of type ‘boost::remove_reference<std::basic_ostream<char>&>::type {aka std::basic_ostream<char>}’
(...)
What is the reason of such a failure? Is there a way to workaround it in C++11 mode?
The code presented above is a simplification of what I use in a generic code. Hence the need for such a combination (boost::move
+ non-const ref as a return value type).
I use gcc v4.6.3, Boost 1.54.0 and Ubuntu 12.04 (i386).
UPDATE 1: I made the test case a bit more realistic. The goal is to have a generic function that calls a functor under a lock and returns the value returned by the functor via boost::move() as the type of the return value might be movable but non-copyable (which is true in some of my invocations of call_under_lock()
).
UPDATE 2: It turns out that a similar problem can be observed when using clang 3.5.1~(exp)
in C++11 mode:
$ clang test.cpp -I../../boost_latest/ -lstdc++ --std=c++11
test.cpp:11:10: error: non-const lvalue reference to type 'basic_ostream<[2 * ...]>' cannot bind to a temporary of type 'basic_ostream<[2 * ...]>'
return boost::move(r);
^~~~~~~~~~~~~~
test.cpp:19:22: note: in instantiation of function template specialization 'call_under_lock<std::basic_ostream<char> &, boost::_bi::bind_t<std::basic_ostream<char> &, std::basic_ostream<char> &(*)(std::basic_ostream<char> &, int, int),
boost::_bi::list3<boost::reference_wrapper<std::basic_ostream<char> >, boost::_bi::value<int>, boost::_bi::value<int> > > >' requested here
std::ostream& os = call_under_lock<std::ostream&>(
UPDATE 3: This topic has also been discussed on boost-users mailing list [1].
What is the reason of such a failure?
In C++03, R is std::ostream&
, and boost::move()
evidently returns the same type, and so there is no problem.
In C++11, R is still std::ostream&
, however boost::move()
will return std::ostream&&
. This makes the expression boost::move()
an rvalue, which can not bind to a non-const lvalue reference.
Is there a way to workaround it in C++11 mode?
Yes:
template <class R, class F>
R call_under_lock(F f) {
// lock();
R r = f();
// unlock();
return r;
}
This should do exactly what you want in both C++03 and C++11. It will move when you want, and not move when you don't want.