2

以下是可用于重现该问题的源代码:

#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));
}

在 GCC 中使用 C++03 模式时,代码编译没有任何问题。另一方面,使用 C++0x 模式会产生以下错误:

$ 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>}’
(...)

这种失败的原因是什么?有没有办法在 C++11 模式下解决它?

上面提供的代码是我在通用代码中使用的简化。因此需要这样的组合(boost::move+ non-const ref 作为返回值类型)。

我使用 gcc v4.6.3、Boost 1.54.0 和 Ubuntu 12.04 (i386)。

更新 1:我使测试用例更加真实。目标是拥有一个通用函数,它在锁下调用函子并通过 boost::move() 返回函子返回的值,因为返回值的类型可能是可移动但不可复制的(在某些情况下是正确的)我对 ) 的调用call_under_lock()

clang 3.5.1~(exp)更新 2:事实证明,在 C++11 模式下使用时可以观察到类似的问题:

$ 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&>(

更新 3:这个话题也在 boost-users 邮件列表 [1] 上讨论过。

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

4

1 回答 1

3

这种失败的原因是什么?

在 C++03 中,R 是std::ostream&,boost::move()显然返回相同的类型,所以没有问题。

在 C++11 中,R 仍然是std::ostream&,但是boost::move()会返回std::ostream&&。这使得表达式boost::move()成为一个右值,它不能绑定到非 const 左值引用。

有没有办法在 C++11 模式下解决它?

是的:

template <class R, class F>
R call_under_lock(F f) {
  // lock();
  R r = f();
  // unlock();
  return r;
}

这应该完全符合您在 C++03 和 C++11 中的要求。想动就动,不想动就不动。

于 2014-02-12T18:30:01.773 回答