|
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).
> 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