Sometimes, generated copy constructor just make you code buggy (when you have member raw pointers, for example). Sometimes you don't really want a copy constructor. I know I can create a private one, but why is it generated? Maybe it would be better to require programmer to specify the need for the generated one (like declaring but not implementing).
Most of times C++ compilers don't generate code and this is why it's so fast and so flexible. Why copy constructors don't follow this philosophy? C-language compatibility?
I've found no mention to this in the Stroustrup's FAQ. Is it so obvious?
Rodrigo Strauss wrote: >Sometimes, generated copy constructor just make you code buggy (when >you have member raw pointers, for example). Sometimes you don't really >want a copy constructor. I know I can create a private one, but why is >it generated? Maybe it would be better to require programmer to specify >the need for the generated one (like declaring but not implementing).
>Most of times C++ compilers don't generate code and this is why it's so >fast and so flexible. Why copy constructors don't follow this >philosophy? C-language compatibility?
I presume it is because of C compatibility, which is a very good reason. I think it is also the fastest solution, because the generated code will probably do a simple memcpy, and there isn't anything faster than that. But I agree that it can make your code buggy.
-- Mark dot Van dot Peteghem at q-mentum dot com http://www.q-mentum.com -- easier and more powerful unit testing
> ...because the > generated code will probably do a simple memcpy, and there isn't > anything faster than that. ...
The generated copy-constructor dose not use the memcpy for copying object. It dose memberwise copy for all the subobjects by the other copy-ctors and (user-defined) assignment operators.
Mark Van Peteghem wrote: >>Sometimes, generated copy constructor just make you code buggy (when >>you have member raw pointers, for example). Sometimes you don't really >>want a copy constructor. I know I can create a private one, but why is >>it generated? Maybe it would be better to require programmer to specify >>the need for the generated one (like declaring but not implementing).
>>Most of times C++ compilers don't generate code and this is why it's so >>fast and so flexible. Why copy constructors don't follow this >>philosophy? C-language compatibility?
> I presume it is because of C compatibility, which is a very good > reason. I think it is also the fastest solution, because the > generated code will probably do a simple memcpy, and there isn't > anything faster than that. But I agree that it can make your code > buggy.
The memcpy semnatics have not been used for a very long time. C++ uses member-wise copy/assignment semantics.
Also, if I recall correctly, the copy/assign generation had to do more with making user defined types behave like builtin types, which can be copied and assigned. That would be the "philosophical" aspect.
As for bugginess: C++ has always generated copy-ctor and op= if the programmer did not define them; so, as long as you keep that in the foreground, instead of the background, it's hard to forget about it and get into the buggy situation. The extra bugginess is usually because the need to manage copy/assign is left as an afterthought, or "discovered" while debugging a [detected] runtime problem. -- A. Kanawati NO.antounk.S...@comcast.net
> The memcpy semnatics have not been used for a very long time. C++ > uses member-wise copy/assignment semantics.
Of course, if the members have simple copy semantics (i.e., the whole thing is POD), the compiler will tend to use a block move that may in fact be faster than the generic memcpy as assumptions can be made about the size and the alignment.
Rodrigo Strauss wrote: > Sometimes, generated copy constructor just make you code buggy (when > you have member raw pointers, for example). Sometimes you don't really > want a copy constructor. I know I can create a private one, but why is > it generated? Maybe it would be better to require programmer to > specify the need for the generated one (like declaring but not > implementing).
> Most of times C++ compilers don't generate code and this is why it's > so fast and so flexible. Why copy constructors don't follow this > philosophy? C-language compatibility?
> I've found no mention to this in the Stroustrup's FAQ. Is it so > obvious?
Most obvious reason is C compatibility. C supports assignment of structs by default, therefore C++ must for (at a minimum) support it for POD types. In practice, supporting it for POD types but not for other types, would be an anomoly in the language for compiler writers to deal with and to frustrate programmers.
The default generated copy constructor for all types is "member-wise copy" semantics. The only cause of inefficiency in that is related to the fact that this means calling user-defined constructors (if any).
AS to it causing bugs: the basic rule of thumb is that anything which involves sharing a raw pointer between two objects can introduce bugs. If you're doing such things, the process of designing the class should identify that it has raw pointers, so there needs to be some thought as to whether it requires a copy constructor to be explicitly written, of if copying should be disabled (eg make the copy constructor private). And, if you do it to a copy constructor, also do it with the assignment operator.....
Rodrigo Strauss wrote: > Sometimes, generated copy constructor just make you code buggy (when > you have member raw pointers, for example). Sometimes you don't really > want a copy constructor. I know I can create a private one...
You can also declare (but not define) a copy constructor (not necessarily private). This will prevent the compiler from generating it, and a linker error will tell you if there are attempts in your code to call it. BTW, if you don't want a copy constructor, you probably do not want an assignment operator either. This is an example:
class CODate { public: ... // declared but not defined: CODate( CODate const& date); CODate& operator=( CODate const& date);
Rodrigo Strauss wrote: > Sometimes, generated copy constructor just make you code buggy (when > you have member raw pointers, for example). Sometimes you don't really > want a copy constructor. I know I can create a private one, but why is > it generated?
It is to preserve the same copy and assignment semantics as primitive types. You would be rudely surprised if objects of a class you define yourself cannot do the same thing as let's say an int.
Looking at it from the opposite side: primitive types like int, float etcetera also have a copy constructor and an assignment operator. It is as if a copy constructor and an assignment operator is generated for them too. You can for example do:
int x(5); int y(x);
which looks very much the same as using a copy constructor to initialize an object, or:
int p = 10; int q = p;
which amounts to using int::operator(int& that) to initialize the value.
Now, in order to prevent surprises, James Coplien tells us that a programmer should implement a default constructor, a copy constructor, an assignment operator and a destructor for any nontrivial class. He calls this 'Orthodox Canonical Form'.
> You would be rudely surprised if objects of a class you define yourself > cannot do the same thing as let's say an int.
No, I would not. After all, my class is not an int. Or, if it is, why stop at copying and assignment? Where is my automatically generated operator+, operator* and other friends? :-) And finally, where is that lovely weird 0 constant that always gets mixed with NULL pointer during overloads? :-)
> Looking at it from the opposite side: primitive types like int, float > etcetera also have a copy constructor and an assignment operator.
They have many other things as well. I think, root of the problem is C structs, not primitive types.
> Now, in order to prevent surprises, James Coplien tells us that a > programmer > should implement a default constructor, a copy constructor, an assignment > operator and a destructor for any nontrivial class. He calls this > 'Orthodox > Canonical Form'.
Well, from modern "defensive" programming point of view this considered a bad thing.
In Java/C# you must know what you're doing, but if you try to do something really stupid (at the low level), it is either impossible, or compiler will warn you.
In C you must know what you're doing, and if you do something stupid, so be it. No stop signs, speed limit, nobody's gonna slow you down :-) However, if you don't explicitly do something stupid, compiler will never silently do it for you. If you did not write it, it's not there. Zero initialization of statics does not count.
In C++ you must know not only what you're doing, but also what compiler is doing on your behalf. If you do something stupid, or if compiler does something stupid and you overlook it, it's your fault.
I am afraid C++ got the worst combination of them all.
> Now, in order to prevent surprises, James Coplien tells us that a > programmer > should implement a default constructor, a copy constructor, an assignment > operator and a destructor for any nontrivial class. He calls this > 'Orthodox > Canonical Form'.
Why is there a default constructor in this list? If another constructor, like a copy constructor, is declared for a class, the compiler will not automatically generate a default constructor, will it?
>>Now, in order to prevent surprises, James Coplien tells us that a >>programmer >>should implement a default constructor, a copy constructor, an assignment >>operator and a destructor for any nontrivial class. He calls this >>'Orthodox >>Canonical Form'.
> Why is there a default constructor in this list? If another constructor, > like a copy constructor, is declared for a class, the compiler will not > automatically generate a default constructor, will it?
Correct. So, if you don't have a default ctor, you will get surprised, unpleasantly, when you try to use the standard containers, some of which impose default-ctor requirements on the element type.
My version of this rules is that I need to address the enumerated methods: as in, if there is no default-ctor, it is not because I forgot about it [usually :-]
The copy/assign/destroy are usually coupled: assign and copy are essentially variations on a theme, and if you need to hand-code either of them, you need to do the other as well. As for the destructor: any time you need to hand-code copy/assign, including inhibiting them, there is a good chance that you will need to write the destructor yourself. Conversely, if you have a hand-coded destructor, it is almost always the case that you will also need to pay extra attention to copy/assign operations. -- A. Kanawati NO.antounk.S...@comcast.net
>No, I would not. After all, my class is not an int. > Or, if it is, why stop at copying and assignment? > Where is my automatically generated operator+, operator* and other friends? > :-)
Well, you're kidding - but what's really missing is automatically-generated operator<. I'm serious :) IMO it should recursively invoke operator< of all members in the order of their declaration to produce a
stable lexicographical sequence. Writing this manually is boring - and this is even impossible to use any metaprogramming tricks here.
> >No, I would not. After all, my class is not an int. >> Or, if it is, why stop at copying and assignment? >> Where is my automatically generated operator+, operator* and other > friends? >> :-)
> Well, you're kidding - but what's really missing is > automatically-generated > operator<. I'm serious :) IMO it should recursively invoke > operator< of all members in the order of their declaration to produce > a
> stable lexicographical sequence. Writing this manually is boring - and > this is > even impossible to use any metaprogramming tricks here.
But this breaks immediately for complex numbers, where you don't have an ordering.
How will the compiler know that name-and-address records should be sorted on the zip code while printing, and by last name while searching?
Antoun Kanawati wrote: > Correct. So, if you don't have a default ctor, you will get surprised, > unpleasantly, when you try to use the standard containers, some of which > impose default-ctor requirements on the element type.
No standard container requires a default constructor. The functions that need to use "fill" objects take an additional argument (defaulted to a default constructed object) to copy to the "empty" position.
Ron Natalie wrote: > Antoun Kanawati wrote: >>Correct. So, if you don't have a default ctor, you will get surprised, >>unpleasantly, when you try to use the standard containers, some of which >>impose default-ctor requirements on the element type.
> No standard container requires a default constructor. The functions > that need to use "fill" objects take an additional argument (defaulted > to a default constructed object) to copy to the "empty" position.
It's only one implementation, but that's all I have to work with. And, it wants a default ctor.
$ cat m.cpp #include <map>
struct Foo { Foo(int) { }
};
int main() { std::map<int, Foo> m; m[3] = Foo(42); return 0;
}
5$ g++ -c m.cpp ..../bits/stl_map.h: In member function `_Tp& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](const _Key&) [with _Key = int, _Tp = Foo, _Compare = std::less<int>, _Alloc = std::allocator<std::pair<const int, Foo> >]': m.cpp:10: instantiated from here ..../bits/stl_map.h:339: error: no matching function for call to `Foo::Foo()' m.cpp:3: note: candidates are: Foo::Foo(const Foo&) m.cpp:4: note: Foo::Foo(int)
Antoun Kanawati <anto...@comcast.net> writes: > Ron Natalie wrote: > > Antoun Kanawati wrote: > >>Correct. So, if you don't have a default ctor, you will get surprised, > >>unpleasantly, when you try to use the standard containers, some of which > >>impose default-ctor requirements on the element type.
> > No standard container requires a default constructor. The functions > > that need to use "fill" objects take an additional argument (defaulted > > to a default constructed object) to copy to the "empty" position.
> It's only one implementation, but that's all I have to work with. > And, it wants a default ctor.
> $ cat m.cpp > #include <map>
> struct Foo { > Foo(int) { } > };
> int main() > { > std::map<int, Foo> m; > m[3] = Foo(42); > return 0; > }
> 5$ g++ -c m.cpp > ..../bits/stl_map.h: In member function `_Tp& std::map<_Key, _Tp, > _Compare, _Alloc>::operator[](const _Key&) [with _Key = int, _Tp = Foo, > _Compare = std::less<int>, _Alloc = std::allocator<std::pair<const int, > Foo> >]': > m.cpp:10: instantiated from here > ..../bits/stl_map.h:339: error: no matching function for call to `Foo::Foo()' > m.cpp:3: note: candidates are: Foo::Foo(const Foo&) > m.cpp:4: note: Foo::Foo(int)
[snip]
As it happens, the standard requires the default constructor be called:
Antoun Kanawati <anto...@comcast.net> wrote in message <news:R8OdnXn7-a-aZMDfRVn-pQ@comcast.com>... > It's only one implementation, but that's all I have to work with. > And, it wants a default ctor.
> $ cat m.cpp > #include <map>
> struct Foo { > Foo(int) { } > };
> int main() > { > std::map<int, Foo> m; > m[3] = Foo(42); > return 0; > }
> 5$ g++ -c m.cpp > ..../bits/stl_map.h: In member function `_Tp& std::map<_Key, _Tp, > _Compare, _Alloc>::operator[](const _Key&) [with _Key = int, _Tp = Foo, > _Compare = std::less<int>, _Alloc = std::allocator<std::pair<const int, > Foo> >]': > m.cpp:10: instantiated from here > ..../bits/stl_map.h:339: error: no matching function for call to `Foo::Foo()' > m.cpp:3: note: candidates are: Foo::Foo(const Foo&) > m.cpp:4: note: Foo::Foo(int)
m[3] has to be constructed out of nothing (using Foo()) before assigning a value...
Thats something i don't like with maps: There is no way to insert a value with "overwrite option" in one simple line if you don't have a default constructor at hand.
You may try:
std::map<int, Foo>::iterator iter = m.find(3); if (iter == m.end()) m.insert(std::pair<int, Foo>(3,Foo(42))); else iter->second = Foo(42);
>>It's only one implementation, but that's all I have to work with. >>And, it wants a default ctor.
[snip] example code showing a std::map::operator[] non-const use-case.
> m[3] has to be constructed out of nothing (using Foo()) before > assigning a value...
The example was in response to the assertion that no standard container requires its elements to have default ctors.
> Thats something i don't like with maps: There is no way to insert a > value with "overwrite option" in one simple line if you don't have a > default constructor at hand.
It could be done by introducing proxies so that the operation maps to either an assignment, or a copy construction. But, that would mean that the return type of operator[] is no longer a reference to the mapped type. And that type has to be crafted so that only transient instances can be made; otherwise, you will have object representing a partial assignment floating around. -- A. Kanawati NO.antounk.S...@comcast.net