ISO/IEC Copy CTOR and (N)RVO [was: Re: [ROOT] Seg.Violation on return TGraph]

From: Christian Holm Christensen (cholm@hehi03.nbi.dk)
Date: Thu Mar 13 2003 - 14:41:15 MET


Hi Rene, 

Rene Brun <Rene.Brun@cern.ch> wrote concerning
  Re: [ROOT] Seg.Violation on return TGraph [Thu, 13 Mar 2003 10:03:46 +0100] 
----------------------------------------------------------------------
> Hi Christian,
> 
> Let me retract part of what I said about illegal C++. I should have
> said "dangerous and inefficient C++"

So true!

> I have my personal view on your RVO, NRVO and other UFOs that you quote
> in your mail. 

UFO's ?! Where? What? How?  :-)

[Just for the record:
   RVO:  Return Value Optimisation 
   NRVO: Named Return Value Optimisation]
 
> I must say that I have a "const" attitude wrt to some "volatile"
> concepts.

A general observation: `volatile' is a greatly overlooked keyword in
C++.  Essentially it can help you out where optimisation may remove
variables that you really _really_ need.  See also another article by 
Andrei Alexandrescu on threads and volatile [1].  

> My CV is "cv-qualified" type and I hope that it will not be
> disqualified by the upcoming "move-semantic" of ISO/IEC C++ standard
> ::) 

LOL.

> The point in my mail was to discourage the use of a return by value
> for objects that can be pretty large. 

Yes, and that is indeed a good point.  Even if your compiler does
(N)RVO, you would face the same issue of a temporary in code that
goes: 

  class foo { 
  private: 
    double* _data; // Biiiig array 
    int     _n;  
  public: 
    foo(int n=1024) : _n(n) 
    { 
      _data = new double[n]; 
    }
    foo(const foo& other) : _n(other._n) 
    { 
      _data = new double[_n];
      *this = other; 
    }
    virtual ~foo() 
    { 
      delete [] _data; 
    }
    foo& operator=(const foo& other) 
    { 
      if (_n != other._n) throw std::logic_error("Incompatible sizes");
      for (size_t i = 0; i < _n; i++) _data[i] = other._data[i];
    } 
    double& operator[](size_t i) 
    { 
      if (i >= _n) throw std::out_of_range("index out of range");
      return _data[i]; 
    }
    const double& operator[](size_t i) const
    { 
      if (i >= _n) throw std::out_of_range("index out of range");
      return _data[i]; 
    }
  };
 
  foo make_foo(int n) 
  { 
    foo temp(n); 
    for (size_t i = 0; i < n; i++) temp[i] = double(i);
    return temp;
  }

  int main() 
  { 
    foo f2;       // Default CTOR called (biiiig object)
    f2 = make_foo(1024);
    return 0;
  }
  
Your compiler has to border on the divine to optimise that code. 

> I have a remark about copy constructors in general. It is often
> unclear what a copy constructor should do. 

Section 12.8 in the ISO/IEC C++ standard say how they should behave in
some circumstances, but you're right in that the semantics is some
times really hard to figure out. 

> Some complex objects like TGraph, TH1, TTree, have collections
> of other objects or references. These objects may be named objects
> in some directory, Hashtable or like. A copy (in the C++ sense) may have
> many side-effects if one is not careful. 

I guess what you refer to, is that it may allocate memory, change
global/static variables, and so on.  The main problem is, that in the
copy CTOR you can't really tell if that member function was called as
a result of returning a value from a (member) function, or because the
programmer did an explicit copy: 
  
  foo f1; 
  foo f2(f1);

In the first case, you may want to do some sort of optimisation, and
have as little as possible side-effects, while in the latter, you
really want to make a `deep copy', probably with some side-effects. 

> This is the reason why we implemented the Clone function that makes
> a deep copy of the object itself including its dependencies and give
> the possibility to give a name to the new cloned object in one
> single operation. 

You could almost do the same with a copy CTOR (a class can have many
copy CTORs): 

  foo(const foo& other); // `Normal' CTOR 
  foo(const foo& other, bool deep); // Deep/shallow copy CTOR 
  foo(const foo& other, bool deep, const std::string& name);  
     // Deep/shallow copy CTOR with a new name. 

Just a thought.

> I have a remark on the "smart" code that you send in your mail. As
> already pointed out by Nick, this code is not portable.

I think I pointed that out pretty well too, in that section I quoted
from the ISO/IEC standard, but that's just details :-) 

> I don't think that one should encourage users to systematically move
> to the latest "modern" way when simpler and more efficient solutions
> are around.

I think what is proposed for the standard revision, and Andrei
Alexandrescu `Mojo' example are things to keep in mind, just because
it's so easy to fall into the pit we've discussed here. 

I'm aware that ROOT is pretty `heap-oriented' (that is, almost
everything is assumed to be a pointer to an object).  However, most
people coming from other OO languages like Java and SmallTalk will not
be really comfortable with that idea, and could very well start coding
something like 

  foo make_foo() { foo f; return f; } 

with all it's pitfalls.  Incidentally,  there's another way to code
that function too: 

  foo& make_foo() { foo* f = new foo; return *f; } 

This will pass a reference to a heap object back to the caller, and no
copies are made.  However, you need to actually delete the heap object
at some point, 'cause when the reference goes out of scope the heap
object isn't automatically freed.  That's of course similar to the 

  foo* make_foo() { foo* f = new foo; return f; } 
  
solution, only that in the latter case it's much more evident that the
caller has to deallocate the returned object. 

And now for something completely different: I stumbled over the
`Boost.Python' [2] library the other day, and thought it might be
worth while for ROOT and CINT to see how they create dictionaries to
call C++ code from an interpretor. One of the cool things is that it's
possible to derive a (python) class from a compiled class.

Yours, 

 ___  |  Christian Holm Christensen 
  |_| |	 -------------------------------------------------------------
    | |	 Address: Sankt Hansgade 23, 1. th.  Phone:  (+45) 35 35 96 91
     _|	          DK-2200 Copenhagen N       Cell:   (+45) 24 61 85 91
    _|	          Denmark                    Office: (+45) 353  25 305
 ____|	 Email:   cholm@nbi.dk               Web:    www.nbi.dk/~cholm
 | |

[1] http://www.cuj.com/experts/1902/alexandr.htm?topic=experts
[2] http://www.boost.org/libs/python/doc/index.html



This archive was generated by hypermail 2b29 : Thu Jan 01 2004 - 17:50:10 MET