2

I'm interested in improving my understanding of how to avoid writing a C++ class that causes problems when copied.

In particular, I've written a class named Policy that I intend to copy. I have not defined a non-default destructor, copy constructor, or copy assignment operator. The only operator I've overloaded is the following:

friend bool operator== (const Policy& p1, const Policy& p2) {
    for(int i=0; i<p1.x.size(); i++) {
        if(p1.x[i] != p2.x[i])
            return false;
    }
    return true;
}

The class's members are either standard data types such as int, double, bool, std::string, std::vector<double>, std::vector<int>, std::vector<string>, or one of a few small (and thus not overly complex) classes I defined that are definitely copyable without any problem. Now, there is a member of Policy that is an instance of the NRRan class, which is a class I built as a wrapper around a non-copyable third-party data type; NRRan is as follows:

class NRRan {
public:
    double doub() { return stream->doub(); }

    int intInterval(const int& LB, const int& UB) { return LB+int64()%(UB-LB+1); }

    void setNewSeed(const long& seed) {
        delete stream;
        stream = new Ranq1(seed);
    }    

    NRRan() { stream = new Ranq1(12345); }    

    ~NRRan() { delete stream; }

    NRRan(const NRRan& nrran) {
        stream = new Ranq1(12345);
        *stream = *(nrran.stream);
    }    

    NRRan& operator= (const NRRan& nrran) { 
        if(this == &nrran)
            return *this;    
        delete stream;
        stream = new Ranq1(12345);
        *stream = *(nrran.stream);    
        return *this;
    }

private:
    Ranq1* stream;  // underlying C-struct 
    Ullong int64() { return stream->int64(); }    
};

But the whole point of the NRRan class is to make Ranq1 copyable. So, given the Policy class as I've described it (sorry, I am unable to post a large part of the code), is there any thing that might cause a problem when I copy Policy? My expectation is that copying will create a perfect value-for-value copy.

A more general way of asking my question is the following: Generally speaking, what types of things can possibly cause problems when copying a class? Is there anything in addition to the "Rule of Three" (or "Rule of Five") to be concerned about when making a class copyable?

synaptik
  • 8,971
  • 16
  • 71
  • 98
  • 1
    You should be fine in this case, I think. All of the Policy class members are copy-able via shallow copy (e.g. assignment operator), including your NRRan class which overloads that operator to copy the *stream pointer properly. The only things you need to worry about is copying pointers. If your class handles things like deleting/generating that pointer, messing with one class could mess with the copy so you would have to duplicate the pointers on copy instead. Other cases, having two classes ref the same pointer is fine as long as they don't delete them. – Lochemage Jun 07 '13 at 23:40
  • Thank you. That's what I was thinking (hoping). – synaptik Jun 07 '13 at 23:42

2 Answers2

1

Well, this question is misleading..

One liner answer can be like -- use std::is_copy_constructible to find out if a class can be copied or not.

g-217
  • 2,069
  • 18
  • 33
  • 1
    This does not work, `std::is_copy_constructible` only checks if the type does not delete the copy constructor. For example `is_copy_constructible>>` will evaluates to true while it's not really copyable. – markand Oct 09 '19 at 09:32
  • Yes, for containers we need bit more robust check. I'll update my answer. – g-217 Oct 09 '19 at 10:11
  • @markand `g++` and `clang++` both report `is_copy_constructible>>` as `false` right now, so it does work finally, at least for `std::unique_ptr` – Kaihaku Sep 15 '21 at 17:13
0

A class is non-copyable if it has a deleted or private copy constructor/assignment operator, or has at least one member with deleted or private copy constructor/assignment operator (applies recursively).

example:

#include <iostream>
#include <type_traits>

class A{

};

class B {
private:
    A a;
    B(const B&) = default;
    B& operator=(const B&) = default;
public:
    B() = default;
};

class NonCopyable {
    int i_;
public:
    NonCopyable() : i_{0}
    {}
    NonCopyable(const NonCopyable&) = delete;
    NonCopyable& operator=(const NonCopyable&) = delete;
};


struct Composable {
    NonCopyable nc;
};

struct C {
    A a;
    B b;
};


int main()
{
    A a;
    A aa{a};                // copy construct
    A aaa = a;              // copy assign
    B b;
    //B bb{b};              // can't
    //B bbb = b;                // can't 
    NonCopyable nc;
    //NonCopyable nc2 = nc; // cannot
    //NonCopyable nc3{nc};  // cannot
    Composable comp;
    //Composable comp2{comp};   // can't copy construct either
    //Composable comp3 = comp;// can't copy assign either

    std::cout << std::boolalpha;
    std::cout << std::is_copy_assignable_v<A> << '\n';
    std::cout << std::is_copy_assignable_v<NonCopyable> << '\n';
    std::cout << std::is_copy_constructible_v<Composable> << '\n';
    std::cout << std::is_copy_constructible<C>::value << '\n';
}

Output

true
false
false
false

Similarly for non-movable..

KeyC0de
  • 4,728
  • 8
  • 44
  • 68