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:
unary_negate
with const typename Predicate::argument_type& x
argument),argument_type
shouldn't be reference even when the actual argument is orboost::function
shouldn't be used with reference arguments?The final context in which reference-collapsing takes place is the use of decltype. As is the case with templates and auto, decltype performs type deduction on expressions that yield types that are either T or T&, and decltype then applies C++11’s reference-collapsing rules.
Reference collapsing is the mechanism that leads to universal references (which are really just rvalue references in situations where reference-collapsing takes place) sometimes resolving to lvalue references and sometimes to rvalue references. It occurs in specified contexts where references to references may arise during compilation.
Universal references always have the form T&& for some deduced type T. Reference collapsing is the mechanism that leads to universal references (which are really just rvalue references in situations where reference-collapsing takes place) sometimes resolving to lvalue references and sometimes to rvalue references.
Universal References in C++11. It’s the same for user-defined types like Widget. A Widget object can be an lvalue (e.g., a Widget variable) or an rvalue (e.g., an object returned from a Widget -creating factory function). The type of an expression does not tell you whether it is an lvalue or an rvalue.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With