Rule of 5 and the need to write a copy ctor etc.

So I am aware of the Rule of 5, and of the rule that if one deletes or defaults any one of the 5, they should do the same for all 5. (Herb Sutter) But not delete the dtor !

I am trying to establish when I need to write my own copy constructors etc.

I was wondering if there were any other considerations to make, apart from the following list (This is the main question)?

1 There is a member pointer that owns something
2 There is dynamically allocated members
3 Whenever an implicit copy ctor et al. is deleted, or not defaulted, or not defined, or not trivial?

With 2, if the member is an STL container, there is no need to worry because the object is on the stack, but it's data is on the heap?

With 3, I am a little confused by the documentation on cppreference:

Implicitly-defined copy constructor

If the implicitly-declared copy constructor is neither deleted nor trivial, it is defined (that is, a function body is generated and compiled) by the compiler if odr-used.

I understand the the "is neither deleted" part, but not the "nor trivial". I was expecting "Not deleted and trivial". It has the same text for the move constructors as well.

I spent ages looking in the C++14 draft standard to find where that came from, the best I could get was:

Section 12.8 note 13

A copy/move constructor that is defaulted and not defined as deleted is implicitly defined if it is odr-
used (3.2) or when it is explicitly defaulted after its first declaration.

I couldn't see where the standard mentioned the need for it not being trivial, or have anything to do with being trivial. I am guessing it should do though, given the definition of trivial.

Anyway I am a novice at reading the standard, I don't have a lawyer's hat that any where near fits, vastly less a barrister's one :+)

http://en.cppreference.com/w/cpp/language/copy_constructor
Last edited on
I should also say that I am testing using type_traits is_constructible and related to see what might be happening in different situations.

http://en.cppreference.com/w/cpp/types/is_constructible
bump :+)
I understand the the "is neither deleted" part, but not the "nor trivial". I was expecting "Not deleted and trivial". It has the same text for the move constructors as well.

If the constructor is trivial, then the constructor's function body is not required since the compiler already knows how to copy/construct/move trivial types (think c-style structs with POD types as members.) To me, this looks like an implementation detail that really isn't relevant to what you're looking for.
Thanks cire for your reply :+)

If the constructor is trivial, then the constructor's function body is not required since the compiler already knows how to copy/construct/move trivial types (think c-style structs with POD types as members.)


Well, that was quite different to what I had imagined the purpose of implicit special member functions to be: to provide code not supplied by the program. It just seemed a natural assumption, I guess, for code to be put in these functions.

Now that I think about it in the C POD struct context, it makes sense: A C compiler must have had a way of doing shallow copy assignment. And cppreference says what you have said, and mentions std::memmove which was one method C programmers used instead of assignment. And if a special member function is defined, it uses direct initialisation. Awesome, that part has gelled now :+)

So does this mean that barring any other considerations, it looks as though we are back to only having to worry about classes with owning pointers as members?

Thanks again for your answer :+)

Regards

> it looks as though we are back to only having to worry about classes with owning pointers as members?

A class that directly manages a low level resource: rule of five.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
namespace unix
{
    struct fd
    {
        fd() noexcept = default ;
        fd( const char* path, int flags ) noexcept : dumb_fd( ::open(path,flags) ) {}

        // rule of five
        ~fd() noexcept { do_close() ; } // 1
        fd( const fd& that ) : dumb_fd( ::dup(that.dumb_fd) ) { /* ... */ } // 2
        fd( fd&& that ) noexcept : dumb_fd(that.dumb_fd) { that.dumb_fd = -1 ; } // 3
        fd& operator= ( const fd& that ) // 4
        { dumb_fd = ::dup2( that.dumb_fd, dumb_fd ) ; /* ... */ return *this ; }
        fd& operator= ( fd&& that ) noexcept // 5
        { if( dumb_fd != that.dumb_fd ) { do_close() ; dumb_fd = that.dumb_fd ; that.dumb_fd = -1 ; } }

        operator int() const { return dumb_fd ; }
        // ...
 
        private:
            int dumb_fd = -1 ;
            void do_close() noexcept { if( dumb_fd != -1 ) ::close(dumb_fd) ; }
    };
}

Note: classes with owning raw pointers are rare in well-written code.


A class that may not directly manage a resource, but is an object-oriented type: rule of five (declared as defaulted)


Any other class: rule of zero

Details: http://en.cppreference.com/w/cpp/language/rule_of_three
@JLBorges

Thanks for your informative reply. :+)

I understand the whole topic more clearly now.

Just to be clear on one point though, by object-oriented type did you mean a class intended for polymorphic use? I gathered this from the section of the linked page which had the 5 defaulted functions. If so, then I understand that part as well.

I tried searching for that term to see what came up, but it seems to be a tricky term to search for.

Cheers
> by object-oriented type did you mean a class intended for polymorphic use?

Yes, a polymorphic class as defined by the IS.
Virtual functions support dynamic binding and object-oriented programming. A class that declares or inherits a virtual function is called a polymorphic class.
Topic archived. No new replies allowed.