Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reference collapsing under C++03

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 with const 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?
like image 270
Jan Hudec Avatar asked Oct 05 '12 11:10

Jan Hudec


People also ask

How does reference collapsing work in C++11?

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.

What is reference-collapsing?

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.

What is the difference between universal references and reference collapsing?

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.

What is universal reference in C++11?

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.


1 Answers

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.

like image 153
Quuxplusone Avatar answered Sep 20 '22 22:09

Quuxplusone