Boost logo

Boost Users :

Subject: Re: [Boost-users] Aliased parameters in boost::bind
From: Gavin Lambert (gavinl_at_[hidden])
Date: 2017-03-30 22:23:56


On 30/03/2017 20:34, Rainer Deyke wrote:
> On 30.03.2017 00:12, Gavin Lambert wrote:
>> const& parameters should be your default for anything with non-trivial
>> copy/move behaviour anyway, so you'd only run into this issue in
>> already-bad code.
>
> Passing by const& can actually force extra copies to be created that
> would not be created with pass-by-value.
>
> Consider:
>
> std::string f();
>
> struct X {
> void by_value(std::string s) {
> this->some_member = std::move(s);
> }
>
> void by_const_ref(std::string const& s) {
> // Cannot move from const&
> this->some_member = s;
> }
>
> std::string some_member;
> };
>
> X x;
>
> // The following line creates no copies.
> x.by_value(f());
>
> // The following line must create an extra copy.
> x.by_const_ref(f());

True, but in my experience it's a rare case. It replaces a single copy
with two moves, and since moves can decay to copies (in types that
implement a copy constructor without a move constructor, which is quite
a lot of older code), it's quite likely to be a pessimisation in the
general case.

Passing by value as a means to take advantage of rvalue move semantics
and copy elision is *only* a good choice if you can prove that all of
the following are true:

   1. The type has a move constructor which is at least twice as fast as
the copy constructor. (This is an easy bar to meet if the copy
constructor needs to allocate heap memory, and borderline for a
shared_ptr copy.)

   2. The body of the method *always* (and I do mean really always)
needs a copy of the parameter (eg. to initialise a member field or
create a lambda/binding -- this is an easy bar to meet for constructors
and setters, but very uncommon for any other method). If there is an
execution path that doesn't need to make that copy, then it's a
pessimisation whenever that path is taken (an avoidable copy -- which
you can usually ignore for exception-throw paths, but not other conditions).

   3. The method is very likely to be called with both lvalue and rvalue
parameters, either equally or with a higher incidence of rvalues. (If
most calls are with lvalues with few or no rvalues, it's typically not
worth it, because that would be replacing a single copy with a copy+move.)

Note that you *can't* meet the first condition for generic
templated-type code -- type traits tell you nothing about performance
and you can't even detect whether a type actually *has* a "real" move
constructor (rather than decaying to a copy constructor). Using
multiple overloads or perfect forwarding is generally the best choice
for generic code instead. And it's hard (though not impossible) to
justify the last condition in library code.


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net