Prev: pros and cons of returning const ref to string instead of string by value
Next: pros and cons of returning const ref to string instead of string by value
From: Francis Glassborow on 3 Dec 2009 00:16 Andrew wrote: > On 2 Dec, 21:59, "A. McKenney" <alan_mckenn...(a)yahoo.com> wrote: >> Warning: I'm not entirely on your side. > >> Another approach if you have reason to worry >> about the inefficiency of copying std::string >> is to "roll your own" > > I want to return by value and I am not worried about the performance > of this. The opposition is worried about performance but has no > figures or any other reason to back this up. It is just "more > efficient". I have run into this attitude many many times. It is quite > normal for people to argue for one construct over another in the name > of performance without even knowing if perfornance is an issue in that > area. > The problem is that performance figures are highly dependant on implementations. We know (not speculation) that copy constructing a string with the kind of implementation that was the norm in the 90's is expensive (so much so that COW was considered a strong contender despite problems with multi-threading). More recently most implementations have switched to using the small string optimisation. That makes copying for small strings much less expensive. The problem with references is ensuring that we do not create a hanging reference. The problem with copying is the potential cost of doing so. Now the real problem comes down to programmers who want to have universal 'rules'. I.e. they do not want to have to think. The situation of returning a string member from a class object is quite different to returning a non-member from a function. Interface design is not for the lazy :) Note that we also need to consider whether the caller may want only the current value or to be able to track the value. That leads to me wondering whether we should not sometimes return a const volatile reference. -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Stuart Golodetz on 3 Dec 2009 00:21 Goran wrote: > On Dec 2, 10:58 pm, Stuart Golodetz > <sgolod...(a)NdOiSaPlA.pMiPpLeExA.ScEom> wrote: >> X x; >> const std::string& r = x.s(); // affected by subsequent x destruction >> std::string s = x.s(); // not affected by subsequent x destruction > > > Whoa, stop, that's wrong! r is __not__ affected by x destruction, > because x gets destroyed __after__ r (sort to speak). Try thinking > like this, instead (that's what standard requires WRT variable > construction-destruction): > > { // x only available in this block > X x; > { // r only available in this block > const std::string& r = x.s(); > { // s only available in this block > std::string s = x.s(); > } > } > } > > As you can see, it's not possible for r to be bad due to destroyed x. > What __is__ possible (and people make that mistake), is that you > somehow copy the reference somehow (to a pointer) and keep that after > x is destroyed, e.g. > > const std::string* p; > { > X x; > const std::string& r = x.s(); // affected by subsequent x > destruction > p = &x; // Whoops! > std::string s = x.s(); // not affected by subsequent x destruction > } > p == "Undefined behavior"; > > Goran. It wasn't supposed to be a literal (or complete) example. Here's a better one: #include <iostream> #include <string> #include <boost/shared_ptr.hpp> using boost::shared_ptr; struct X { std::string m_s; X() : m_s("Wibble") {} const std::string& s() const { return m_s; } }; int main() { shared_ptr<X> x(new X); const std::string& r = x->s(); x.reset(); // BOOM std::cout << r << '\n'; return 0; } Regards, Stu -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Martin B. on 3 Dec 2009 00:21 Andrew wrote: > On 2 Dec, 22:00, Zachary Turner <divisorthe...(a)gmail.com> wrote: >> On Dec 2, 7:06 am, Andrew <marlow.and...(a)googlemail.com> wrote: >> >> Maybe I'm overlooking something, but I see little reason to return by >> value. What matters is whether you assign it to a const reference or >> to a value. > > The view I am running into is that a function should return a const > ref to a string so that it can be assigned to a const ref to a string. > The argument is that doing this avoids string copying. I should have > made that clearer. > I don't like that argument. If I need to hold a string I need to hold a string and not some reference to some internals of some object. Note also that: class Foo { string s_; public: string get_s() { return s; } string const& access_s() { return s; } }; .... string x = obj.get_s(); string y = obj.access_s(); // Because of RVO, these two calls will copy the string exactly one time, so ret-by-val does not decrease performance string const& rz = obj.access_s(); // Generally saves one heap allocation, so is faster. // However validity of rz is tightly coupled to state and lifetime of obj, so the likelihood of crashes increases. br, Martin -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Stuart Golodetz on 3 Dec 2009 01:20 Goran wrote: > On Dec 2, 10:58 pm, Stuart Golodetz > <sgolod...(a)NdOiSaPlA.pMiPpLeExA.ScEom> wrote: >> X x; >> const std::string& r = x.s(); // affected by subsequent x destruction >> std::string s = x.s(); // not affected by subsequent x destruction > > > Whoa, stop, that's wrong! r is __not__ affected by x destruction, > because x gets destroyed __after__ r (sort to speak). Try thinking > like this, instead (that's what standard requires WRT variable > construction-destruction): > > { // x only available in this block > X x; > { // r only available in this block > const std::string& r = x.s(); > { // s only available in this block > std::string s = x.s(); > } > } > } > > As you can see, it's not possible for r to be bad due to destroyed x. > What __is__ possible (and people make that mistake), is that you > somehow copy the reference somehow (to a pointer) and keep that after > x is destroyed, e.g. > > const std::string* p; > { > X x; > const std::string& r = x.s(); // affected by subsequent x > destruction > p = &x; // Whoops! > std::string s = x.s(); // not affected by subsequent x destruction > } > p == "Undefined behavior"; > > Goran. To clarify my original post, what I was getting at was that r would be affected were x destroyed somehow. I didn't mean to imply that all the variables were within one function - my bad. I guess this is why minimal, complete code examples are recommended (because what is effectively pseudo-code can be misleading). Stu -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Nevin :-] Liber on 3 Dec 2009 07:50
In article <6LednaHXT5qNForWnZ2dnUVZ8mWdnZ2d(a)bt.com>, Francis Glassborow <francis.glassborow(a)btinternet.com> wrote: > Note that we also need to consider whether the caller may want only the > current value or to be able to track the value. That leads to me > wondering whether we should not sometimes return a const volatile reference. Could you elaborate on why one would need volatile? As you well know, all that const reference means is that the intention is not to modify the object through this alias (mutable, const_cast, etc. notwithstanding, of course). -- Nevin ":-)" Liber <mailto:nevin(a)eviloverlord.com> 773 961-1620 [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |