Boost logo

Boost :

From: Kevin S. Van Horn (Kevin.VanHorn_at_[hidden])
Date: 2002-11-01 10:24:42


I wrote:

> What you're missing here is that if I, as the implementor, declare
> the argument to bee foo_t const &, it will be because I do not
> intend to make a copy, and there are then no copy efficiency
> concerns for you, the user, to worry about. On the other hand, if I
> declare it to be foo_t, this will only be because I need to create
> _and_ _take_ _ownership_ _of_ an actual copy

to which David Abrahams responded:

> I think you're confused. The copy is owned by the virtual machine,
> since it gets created on the stack. Usually, "take ownership of" only
> applies to heap objects whose lifetimes can be managed by their
> owners.

Not necessarily: consider use of swap and move constructors (or, more
properly, Andrei's MOJO techique for approximately implementing move
constructors). Both of these allow us to "take ownership of" a temporary, or
at least its internals. (OK, strictly speaking, this is not what is usually
meant by "take ownership"; I guess I should have chosen my terminology more
carefully.) This all goes back to the discussion Andrei started a couple of
weeks ago about move constructors and MOJO. For example, consider the
canonical strongly exception safe assignment operator:

  X& operator=(X const & x) {
    X tmp(x);
    tmp.swap(*this);
    return *this;
  }

As Andrei pointed out, if a temporary gets bound to x, then we have two
copies. An alternative is

  X& operator=(X x) {
    x.swap(*this);
    return *this;
  }

which involves fewer copies is a temporary gets bound to x, and the same
number of copies as the first implementation for "x1 = x" if x is an lvalue.

The point of all this is that I, as an implementor, should have the freedom to
fiddle with such implementation details without changing the specification of
the class, and I cannot do this if the signature of the operation is fixed in
the specification instead of using valid expressions to specify the operation.

Glen Knowles added:

> But I see little point, is there any practical difference between:
>
> void f(const T& a) {
> T work(a);
> // play with work copy
> }
>
> and
>
> void f(T work) {
> // play with work
> }
>
> for types where const T& would be considered? (larger then a pointer
> and/or with an explicit constructor). Both forms copy construct "work".

The difference lies in the case f(e), where e is an rvalue (e.g., an
expression such as makeT(), where makeT is declared as "T makeT();". In this
case your first alternative involves an extra copy.


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