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