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::functions 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_types, 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