Reference collapsing under C++03
-
05-07-2021 - |
题
I need to create a predicate from bound member function, so I wrapped it in a boost::function<bool(SomeObject const &)>
. That seems to be fine and everything, but I also needed to negate it in one case. However
boost::function<bool(SomeObject const &)> pred;
std::not1(pred);
does not compile under MSVC++ 9.0 (Visual Studio 2008), complaining that reference to reference is invalid:
C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\include\functional(213) : warning C4181: qualifier applied to reference type; ignored
C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\include\functional(213) : error C2529: '_Left' : reference to reference is illegal
The problem is that boost::function
defines the argument_type
as SomeObject const &
and the std::unary_negate<_Fn1>
instantiated by std::not1
internally tries to use const typename _Fn1::argument_type&
and compiler rejects it because T::argument_type
is already a reference. I am certain that that should compile under C++11, but this is old compiler that is C++03 only. So I'd like to know who's fault it is:
- the compiler's, because it should collapse the reference (apparently not),
- the standard library's, because it should be prepared to handle functors taking references (apparently not, because the specification defines
unary_negate
withconst typename Predicate::argument_type& x
argument), - boost's, because
argument_type
shouldn't be reference even when the actual argument is or - mine, because
boost::function
shouldn't be used with reference arguments?
解决方案
The fault is certainly not Boost's; boost::function
is basically just std::function
, with all the same semantics. And boost::function
s with reference parameters work fine, too. You just can't use them with std::not1
or the rest of the <functional>
stuff.
C++11's reference-collapsing makes std::not1
work the way you would think it ought to. The way std::not1
was specified in C++03 couldn't possibly work without reference-collapsing — except in implementations where the implementors did a little bit of creative interpretation rather than slavishly following the letter of the Standard.
It's possible to make std::not1
work in C++03 by adding a specialization of std::unary_negate
for predicates with reference argument_type
s, but neither libc++ nor libstdc++ has done so.
But you know who has? Boost! If you just change your code to use boost::not1
everywhere you currently use std::not1
, everything will work fine. Basically, think of the boost
namespace as if it were a C++11-compliant version of std
; anything that works in C++11's std
namespace probably works in C++03's boost
namespace.
Caveat, hopefully off-topic: The Clang compiler on my Macbook (Apple LLVM version 4.2 (clang-425.0.28) (based on LLVM 3.2svn)
) silently collapses references even in -std=c++03
mode, so that
typedef const int& ref;
typedef const ref& ref2;
produces no error. When you test your C++03 code, make sure you're not using a compiler with this misfeature.