Boost logo

Boost :

Subject: Re: [boost] rvalue ref best practices?
From: Ion Gaztañaga (igaztanaga_at_[hidden])
Date: 2012-06-12 19:08:26


El 12/06/2012 18:18, Dave Abrahams escribió:

> Passing by reference always defeats copy elision. Why would you want to
> do that?

Because the in-out reference (I'm talking about non-const references
instead of returning by value) might have enough memory/resources to
hold the produced value. An example:

1) s has enough capacity, potentially a single allocation

std::string s;
s.reserve(largest_path); //one allocation
std::string ws;
ws.reserve(largest_path); //one allocation

for( N times )
{
   calculate_unix_path(s, directory_name, filename[i]...);//no allocation
   ws = to_wide_string(s); //no allocation
   print_s(ws);
}

2) s's resources not reused, N*2 allocations

std::string s;
std::string ws;
for( N times )
{
    //allocation, s's previous memory discarded
    s = calculate_unix_path(directory_name, filename[i]);
    //w's previously memory discarded
    ws = to_wide_string(s);
    print_s(ws);
}

>> You can catch temporaries by rvalue references to reuse external
>> resources. For generic code, catching by value could do some premature
>> pessimization.
>
> Which pessimization is that?

Example: when copy-construction + swap is more expensive than assignment:

template<class T>
class any_holder
{
    public:
    any_holder &operator=(any_holder<T> taken_by_value)
    {
       using std::swap;
       swap(t, taken_by_value);
       return *this;
    }

    private:
    T t;
}

typedef any_holder< vector<std::string> > any_complex_t;

any_complex_t complex, complex2;
modify_complex(complex);
modify_complex(complex2);

//complex2::t's resources
//not reused: taken_by_value
//was copy-constructed + swapped
//with t instead of copy-assigned
complex2 = complex;

If any_holder implements copy assignment taking a const reference, t's
(vector's) resources are reused.

>> Passing by value has also another downside: it makes your binary
>> interface dependent on the function implementation (do I copy the
>> parameter?). If I change the implementation I need to break ABI.
>
> I don't see how that follows. Leave the ABI alone. Pass-by-value
> completely insulates the caller from the callee.

If I modify my implementation because I no longer need the internal copy
(e.g. the copy was required by an internal function/helper function), I
can't optimize the code and change the signature of the function to take
a reference, as name mangling changes and I'll need to recompile the
caller. If I offer both lvalue and rvalue reference variants I type a
bit more but I can safely change the implementation.

This might sound a bit silly, but I don't like the idea of defining an
interface because I know what I'll do in the implementation details.

>> I'm really worried about "pass by value is free" and "return by value
>> is free" statements I'm hearing in some C++11 panels.
>
> Where are you hearing that? I've heard Scott Meyers complain about that
> too, but as far as I can tell, it's a misunderstanding of what's being
> said.

Apart from some non-really expert C++11 talks I'm attending I remember
this issue in the C&B 2011 Panel with Meyers, Alexadrescu and Sutter,
(around minute 41).

http://channel9.msdn.com/Shows/Going+Deep/C-and-Beyond-2011-C11-Panel-Scott-Meyers-Andrei-Alexandrescu-and-Herb-Sutter

> An example of why, how, and where you want this would be helpful,
> because I fail to grasp the motivation for something like this. Also, I
> think you should take it to std-proposals_at_[hidden] :-)

Yes, sorry, I think it was a bit off topic. One reason is to have
multiple return values without packing them on the callee and unpacking
them in the caller. Another reason is to have a single implementation
that can reuse resources of possibly constructed N out arguments without
the need of a lot of overloads.

void produce_abcd
   ( A &a default{1}, B &b default(2)
   , C &c default(3), D &d default(4))
{
    //a, b, c, d might be references
    //to external objects or references to values
    //constructed with the "default" construction.
    //
    //write code as if a, b, c, d were references
    //to already existing external objects.
    //...

    //If a was externally constructed, resources
    //are reused.
    a = ...
}

//Build four new objects
[A a, Bb, C c, D c] -> produce_abcd(a, b, c, d),

//build new objects and reuse a&b resources
[C c2, D c2] -> produce_abcd(a, b, c2, d2),

//build new objects reusing a, b, c, d
produce_abcd(a, b, c, d),

I'm not sure if benefits outweigh the added complexity and such a
proposal would have some difficult areas, specially because I'm don't
think there is a way to make current pointer to [member]function
implementations compatible with that calling convention (the caller must
pass the "state" of the arguments in runtime, as a hidden parameter), or
without pessimizing pointer to [member]functions. I don't think I could
write such a proposal without help from compiler experts.

Regards,

Ion


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