Why does assigning the return value of boost::move() to a non-const reference fails in C++0x mode but works in C++03 mode

StackOverflow https://stackoverflow.com/questions/21138869

  •  28-09-2022
  •  | 
  •  

Question

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].

[1] http://boost.2283326.n4.nabble.com/move-differences-between-results-in-C-03-and-C-11-modes-td4659264.html

Was it helpful?

Solution

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top