| Register | FAQ | Calendar | Search | Today's Posts | Mark Forums Read |
|
#11
| |||
| |||
| Martin T. wrote: [...] > EXAMPLE CODE: > class Copyable { > public: > Copyable() > : p_(NULL), > s_(0) > { } > > explicit Copyable(size_t s) > : p_(NULL), > s_(0) > { > p_ = new int[s]; > s_ = s; > } > > ~Copyable() > { > delete p_; Wrong. Correct is: delete[] p_; > } [...] One reason never to use new[]. -- Thomas [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
#12
| |||
| |||
| On Sep 4, 3:37 pm, David Abrahams <d...@boostpro.com> wrote: > on Thu Sep 04 2008, Ulrich Eckhardt <doomster-AT-knuut.de> wrote: > > > I guess the alternative that Maciej was thinking about (but never actually > > mentioned) was the copy and swap approach: > > > operator=( T const& other) > > { > > T tmp(other); // make a copy > > swap( *this, tmp); > > return *this; > > } > > That's the wrong way to write it, though ;-). On real compilers > > operator=( T other ) > { > swap( *this, other); > return *this; > } > > can be *much* more efficient because of copy elision. > > Cheers, > > -- > Dave Abrahams > BoostPro Computinghttp://www.boostpro.com > > [ Seehttp://www.gotw.ca/resources/clcm.htmfor info about ] > [ comp.lang.c++.moderated. First time posters: Do this! ] I just reviewed the standard to make sure I didn't forget something, but I'm totally not seeing what you're talking about David Abrahams. I see exactly copy constructor call in both your example code and in Ulrich Eckhardt's code. The single copy constructor cannot be elided because the temporary needs to be created to be swapped with *this. As an aside, the way I've generally done it is as follows, which I assume to be equivalent to the two previous examples. struct T { T& operator= (T const& x) { T(x).swap(*this); return *this; } void swap(T& x); }; -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
#13
| |||
| |||
| JoshuaMaurice@gmail.com wrote: > On Sep 4, 3:37 pm, David Abrahams <d...@boostpro.com> wrote: >> on Thu Sep 04 2008, Ulrich Eckhardt <doomster-AT-knuut.de> wrote: >> >>> I guess the alternative that Maciej was thinking about (but never actually >>> mentioned) was the copy and swap approach: >>> operator=( T const& other) >>> { >>> T tmp(other); // make a copy >>> swap( *this, tmp); >>> return *this; >>> } >> That's the wrong way to write it, though ;-). On real compilers >> >> operator=( T other ) >> { >> swap( *this, other); >> return *this; >> } >> >> can be *much* more efficient because of copy elision. >> >> Cheers, { quoted signature and banner removed -mod } > I just reviewed the standard to make sure I didn't forget something, > but I'm totally not seeing what you're talking about David Abrahams. I > see exactly copy constructor call in both your example code and in > Ulrich Eckhardt's code. The single copy constructor cannot be elided > because the temporary needs to be created to be swapped with *this. > > > As an aside, the way I've generally done it is as follows, which I > assume to be equivalent to the two previous examples. > > struct T > { > T& operator= (T const& x) > { > T(x).swap(*this); > return *this; > } > void swap(T& x); > }; That's the politically correct version. It turns out that, when a function needs to make a copy of its argument, it better takes it by value in the first place. That way, if an rvalue is passed in, the compiler can directly fuse the rvalue to the parameter thus saving a copy. See http://www.erdani.org/publications/cuj-02-2003.html. In short, again, whenever a function plans to make a copy of its argument, it is correct and recommendable to just have the function take the argument by value. That includes the assignment operator. I even managed to convince Herb to introduce that rather unusual (at that time) recommendation in the C++ Coding Standards book. Alas, that rule cannot engulf the copy constructor. I believe that's a rather gratuitous limitation. Many, many, very many things would have been simpler had that limitation not been in place, sigh. Andrei -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
#14
| |||
| |||
| On Sep 5, 2:34 am, Andrei Alexandrescu <and...@cs.washington.edu> wrote: > JoshuaMaur...@gmail.com wrote: > > On Sep 4, 3:37 pm, David Abrahams <d...@boostpro.com> wrote: > >> on Thu Sep 04 2008, Ulrich Eckhardt <doomster-AT-knuut.de> wrote: > > >>> I guess the alternative that Maciej was thinking about (but never actually > >>> mentioned) was the copy and swap approach: > >>> operator=( T const& other) > >>> { > >>> T tmp(other); // make a copy > >>> swap( *this, tmp); > >>> return *this; > >>> } > >> That's the wrong way to write it, though ;-). On real compilers > > >> operator=( T other ) > >> { > >> swap( *this, other); > >> return *this; > >> } > > >> can be *much* more efficient because of copy elision. > > >> Cheers, > > { quoted signature and banner removed -mod } > > > > > I just reviewed the standard to make sure I didn't forget something, > > but I'm totally not seeing what you're talking about David Abrahams. I > > see exactly copy constructor call in both your example code and in > > Ulrich Eckhardt's code. The single copy constructor cannot be elided > > because the temporary needs to be created to be swapped with *this. > > > As an aside, the way I've generally done it is as follows, which I > > assume to be equivalent to the two previous examples. > > > struct T > > { > > T& operator= (T const& x) > > { > > T(x).swap(*this); > > return *this; > > } > > void swap(T& x); > > }; > > That's the politically correct version. It turns out that, when a > function needs to make a copy of its argument, it better takes it by > value in the first place. That way, if an rvalue is passed in, the > compiler can directly fuse the rvalue to the parameter thus saving a copy. > > Seehttp://www.erdani.org/publications/cuj-02-2003.html. In short, > again, whenever a function plans to make a copy of its argument, it is > correct and recommendable to just have the function take the argument by > value. That includes the assignment operator. I even managed to convince > Herb to introduce that rather unusual (at that time) recommendation in > the C++ Coding Standards book. Ok. I see and agree. > Alas, that rule cannot engulf the copy constructor. I believe that's a > rather gratuitous limitation. Many, many, very many things would have > been simpler had that limitation not been in place, sigh. Now, this makes no sense. What is your alternative, a copy constructor that takes arguments by value? But how is that argument object constructed? A copy constructor must take its argument by reference. There has to be some code somewhere which defines how to copy your object. That code must take its input by reference as it cannot take the input by value. That code is the copy constructor. -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
#15
| |||
| |||
| JoshuaMaurice@gmail.com wrote: > On Sep 5, 2:34 am, Andrei Alexandrescu <and...@cs.washington.edu> >> Alas, that rule cannot engulf the copy constructor. I believe that's a >> rather gratuitous limitation. Many, many, very many things would have >> been simpler had that limitation not been in place, sigh. > > Now, this makes no sense. What is your alternative, a copy constructor > that takes arguments by value? But how is that argument object > constructed? A copy constructor must take its argument by reference. > > There has to be some code somewhere which defines how to copy your > object. That code must take its input by reference as it cannot take > the input by value. That code is the copy constructor. I'm very glad somebody answered, because I think this is an interesting subject ).The argument you are making is a form of infinite regression, e.g. if your copy constructor takes a copy by value, then how do you construct that copy, and how that that copy construct its argument... ad infinitum. It is the same as the justification of the original copy constructor design. I think the argument is fallacious. The regression does not occur when an rvalue is at the origin. So the only need is to allow creation of an rvalue, after which the by-value copy constructor can operate on it. True, a by-value copy constructor could not copy lvalues, but that is not a fatal limitation, it is just a useful limitation that the original design was not aware of. Furthermore, overloading can properly take care of efficient construction from both lvalues and rvalues. Does auto_ptr sound familiar? ) Consider a new rule in which by-valuecopy constructor is allowed with the semantics that it only accepts rvalues. Then: template <class T> class auto_ptr { T * p; public: auto_ptr(auto_ptr another) { p = another.p; another.p = 0; } auto_ptr(T * ptr) { p = ptr; } ... }; Now let's give it a test drive: auto_ptr<int> a(new int); // fine, copy ctor from int auto_ptr<int> b = a; // error! nonexisting auto_ptr(auto_ptr&) In other words, the semantics is quite what auto_ptr tried to attain with its odd implementation. There are a few simple rules to add, such as automatically transforming an lvalue into an rvalue upon returning a local from a function. Had this rule be in place in the beginning, the entire rvalue reference proposal would not have been needed. As it turns out, the language has since made enough turns to make the rule above introduce issues with existing programs (Howard Hinnant explained me, I forgot the details but it has something to do with overloading). Now say you want to define a value type that is initializable with both rvalues and lvalues. Then you'd define: struct Val { Val(Val rvalue) { ... use the rvalue as you wish ... } Val(const Val& lvalue) { ... copy lvalue state ... } ... }; You can also distinguish const lvalues from non-const lvalues if that's needed, too. Andrei -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
#16
| |||
| |||
| on Sat Sep 06 2008, Andrei Alexandrescu <SeeWebsiteForEmail-AT-erdani.org> wrote: > Had this rule be in place in the beginning, the entire rvalue reference > proposal would not have been needed. Wrong. You still need perfect forwarding. -- Dave Abrahams BoostPro Computing http://www.boostpro.com [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
#17
| |||
| |||
| David Abrahams wrote: > on Sat Sep 06 2008, Andrei Alexandrescu <SeeWebsiteForEmail-AT-erdani.org> wrote: > >> Had this rule be in place in the beginning, the entire rvalue reference >> proposal would not have been needed. > > Wrong. You still need perfect forwarding. That segues into an issue different from copy construction. Namely, the language design decision causing the lack of perfect forwarding is: references are not first-class and therefore (naturally) they are not automatically deduced for lvalues in a template function call. To clarify: template <class T> void foo(T a) {} will only bind T to a non-reference type (one without a trailing "&") regardless of how it's called. This is a good rule because otherwise people will get confused that in a call-by-value language they get pass-by-reference in templates. Yet sometimes there is a need to say, "if x is an int lvalue bind T = int&. If x is a const int lvalue bind T to T = const int&. If x is an int rvalue bind T to T = int." (The rvalue const int case is not necessary in C++ because const is not transitive.) That particular case could be solved in a number of ways. Assuming that copy efficiency is taken care of by allowing by-value copy constructors, the case can be solved without adding a new type by allowing an "optional reference" as a storage class. For example: template <class T> void foo(T &? a) {} indicates that the compiler should generate two foo()s if needed, one with & and one without. The remaining problem is, what if foo wants to return one of its arguments. Here's where auto could come into play to deduce the result type of the function. This all would be quite useless "what if" talk, so normally I'd spare you all of that, but D2 will implement perfect forwarding quite exactly as outlined above. Which does not make it a much more useful talk to someone not interested in D, but at least takes the "what if" out ).To tie this discussion with the one on max, the implementation of max in D2 would be: auto max(T1, T2)(ref? T1 a, ref? T2 b) { return b > a ? b : a; } Andrei -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
#18
| |||
| |||
| Andrei Alexandrescu wrote: > David Abrahams wrote: >> on Sat Sep 06 2008, Andrei Alexandrescu >> <SeeWebsiteForEmail-AT-erdani.org> wrote: >> >>> Had this rule be in place in the beginning, the entire rvalue reference >>> proposal would not have been needed. >> >> Wrong. You still need perfect forwarding. > > That segues into an issue different from copy construction. Namely, the > language design decision causing the lack of perfect forwarding is: > references are not first-class and therefore (naturally) they are not > automatically deduced for lvalues in a template function call. > > To clarify: > > template <class T> void foo(T a) {} > > will only bind T to a non-reference type (one without a trailing "&") > regardless of how it's called. Before someone else points it out: explicit instantiation can force T to be a reference type. What I meant was that passing lvalues vs. rvalues won't make a difference to foo when deducing T. Andrei -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
#19
| |||
| |||
| Andrei Alexandrescu wrote: > JoshuaMaurice (at) gm...com wrote: >> As an aside, the way I've generally done it is as follows, which >> I assume to be equivalent to the two previous examples. >> >> struct T >> { >> T& operator= (T const& x) >> { >> T(x).swap(*this); >> return *this; >> } >> void swap(T& x); >> }; > > That's the politically correct version. It turns out that, when a > function needs to make a copy of its argument, it better takes it by > value in the first place. That way, if an rvalue is passed in, the > compiler can directly fuse the rvalue to the parameter thus saving a > copy. Do you think this is still relevant when operator= is inline, and it's just doing "T(x).swap(*this)"? If so, I might consider submitting another ticket regarding boost::function, which still copy-assigns in the "politically correct" way at the moment. As so many of us do :-) > See http://www.erdani.org/publications/cuj-02-2003.html Thanks! Looking a the example of passing the result of MakeUrl() to the function Connect(const String& url), you wrote: "For a compiler to optimize away the copy, it has to do the Herculean job of (1) getting access to Connect's definition (hard with separately compiled modules), (2) parse Connect's definition to develop an understanding of it, and (3) alter Connect's behavior so that the temporary is fused with finalUrl." Now is it still such a Herculean job for a compiler to do so for an /inline/ assignment operator that's only just swapping a temporary copy? Honestly, I haven't done any profiling on this, so I just hope you did so already :-) Kind regards, Niels -- Niels Dekker http://www.xs4all.nl/~nd/dekkerware Scientific programmer at LKEB, Leiden University Medical Center -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
#20
| |||
| |||
| on Mon Sep 08 2008, "Niels Dekker - no return address" <noreply-AT-this.is.invalid> wrote: > Andrei Alexandrescu wrote: >> JoshuaMaurice (at) gm...com wrote: >>> As an aside, the way I've generally done it is as follows, which >>> I assume to be equivalent to the two previous examples. >>> >>> struct T >>> { >>> T& operator= (T const& x) >>> { >>> T(x).swap(*this); >>> return *this; >>> } >>> void swap(T& x); >>> }; >> >> That's the politically correct version. It turns out that, when a >> function needs to make a copy of its argument, it better takes it by >> value in the first place. That way, if an rvalue is passed in, the >> compiler can directly fuse the rvalue to the parameter thus saving a >> copy. > > Do you think this is still relevant when operator= is inline, and it's just > doing "T(x).swap(*this)"? Absolutely. The compiler is allowed to elide the implicit copy when the argument is an rvalue if the corresponding parameter is taken by-value. However, when you write T(x) and x is an lvalue (as it is inside the operator= above), the compiler is required to honor your explicit instruction to make a copy of x. > If so, I might consider submitting another ticket regarding > boost::function, which still copy-assigns in the "politically correct" > way at the moment. As so many of us do :-) Could you do that for all the other libraries too? I'm serious, I've been meaning to make a sweep across the source code to fix this. >> See http://www.erdani.org/publications/cuj-02-2003.html > > Thanks! Looking a the example of passing the result of MakeUrl() to the > function Connect(const String& url), you wrote: "For a compiler to optimize > away the copy, it has to do the Herculean job of (1) getting access to > Connect's definition (hard with separately compiled modules), (2) parse > Connect's definition to develop an understanding of it, and (3) alter > Connect's behavior so that the temporary is fused with finalUrl." > > Now is it still such a Herculean job for a compiler to do so for an /inline/ > assignment operator that's only just swapping a temporary copy? Honestly, I > haven't done any profiling on this, so I just hope you did so already :-) Yes, it's still a fairly herculean job. The compiler would have to prove that the entire program works exactly the same way with or without the copy, proving that nothing about the address or identity of the new T is significant. While it's possible that someone might implement that optimization for some cases where T's copy constructor, swap, and destructor are all inlined, that level of understanding is not typically developed in optimizers. In the case of copy-elision for by-value arguments and return values, the compiler is explicitly allowed to _assume_ there is no semantic difference between the original rvalue and its copy. That's low-hanging fruit for a compiler writer, that pays huge dividends. -- Dave Abrahams BoostPro Computing http://www.boostpro.com [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
![]() |
| Thread Tools | |
| Display Modes | |
In an effort to better serve ads to our visitors, cookies are used on objectmix.com. For more information, check out our Privacy Policy.