Boost logo

Boost :

Subject: [boost] Fwd: Re: [move] You can do it better: implementing push_back and handling implicit conversions
From: Jeffrey Lee Hellrung, Jr. (jhellrung_at_[hidden])
Date: 2011-03-08 20:25:42


[My original reply went directly to Ion, due to me being cc'ed in the
original email. Hence forwarding... (pun intended)]

-------- Original Message --------
Subject: Re: [move] You can do it better: implementing push_back and
handling implicit conversions
Date: Tue, 08 Mar 2011 17:23:58 -0800
From: Jeffrey Lee Hellrung, Jr. <jhellrung_at_[hidden]>
Reply-To: jhellrung_at_[hidden]
To: Ion Gaztañaga <igaztanaga_at_[hidden]>

On 3/8/2011 1:57 PM, Ion Gaztañaga wrote:
> Hi to all,
>
> Are you a C++ expert? Do you like Boost.Move? I have a challenge for you.
>
> During Boost.Move review, Jeffrey Lee Hellrung suggested adding
> "techniques to capture (implicitly created) rvalues with emulated rvalue
> references in C++03, e.g., for push_back, when you have knowledge of the
> types of objects you want to capture".

Yes, you know, I do seem to remember suggesting that...

> I've worked on this problem these days and the solution is less than
> obvious (several compilers have has problems with
> ::boost::is_convertible and non-copyable types). I found a solution
> (attached test and required boost/move/move.hpp header) after several
> tries but it's complex (it needs several overloads and enable_if tricks)
> but I'm sure boosters will find a easier solution ;-)

Yes, a bit more complex than the solution I've been using for a while
now (rough idea attached and explained below).

> Current solution seems to work on these compilers: MSVC 7.1, 8.0, 9.0,
> Intel 11.0, GCC 4.3.4, 4.4, 4.4 c++0x mode, 4.5, 4.5 c++0x mode.

I've gotten your test cpp to compile and run with no assertion failures
on MSVC9 (with a slight modification to assertions; see below), and I've
used this same technique on gcc, but I don't work nearly as much on gcc,
so it may be that some corner cases fail.

> Problem: MSVC 10.0 (Visual 2010) with real rvalue references seems to
> have bugs and does not compile the test. I can't find a workaround, a
> future Service Pack might solve these errors, but maybe we should stick
> to emulation code in this compiler, unless someone could shed some light
> on this, of course.

Well...that sucks :(

> Waiting your proposals,
>
> Ion

Okay, here is the summary.

Ion, I count 5 overloads of push_back in C++03 and 4 overloads in C++0x
for your solution. I believe one can get by with just 3 overloads in
C++03, and 1 in C++0x, but the path to "T construction" in my solution
is a little bit different than yours. I delay any T construction until
the placement new statement in priv_push_back. I don't know what your
requirements are in general, but for push_back, I think this should be
fine. The solution I sent you has maybe a slight disadvantage over
yours in that conversion errors would likely end up pointing to
priv_push_back, but if this is a concern, the template overloads of
push_back can be restricted (further) with enable_if (I think).

This has the effect that push_back'ing a conversion_source object x
directly constructs the storage with x, rather than constructing a
temporary and copying or constructing a temporary and moving. For
push_back, it seems this is definitely desirable, and I'm having a hard
time coming up with a situation where it is *not* desirable to delay the
construction as long as possible. In any case, this explains the change
in your assertions. I also added a Default value to the
ConstructionType enum because...it was bugging me ;)

The 3 overloads in C++03 are basically:

      // captures const lvalues and rvalues that are *not* T
      template< class U >
      typename boost::disable_if<
          is_same_sans_const_sans_reference<U,T>
>::type
      push_back(const U& x) { ... as_lvalue(x) ... }

      // captures non-const lvalues and T lvalues
      template< class U >
      void
      push_back(U& x) { ... x ... }

      typedef typename add_reference_add_const<
          typename add_rvalue_reference<T>::type
>::type rv_param_type;

      // captures T rvalues (if T is movable)
      void
      push_back(rv_param_type x) { ... x ... }

In C++0x, a single overload is sufficient, I think:

      template< class U >
      void
      push_back(U&& x) { ... boost::forward<U>(x) ... }

Let me know what you think of this framework.

- Jeff




Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk