Search code examples
c++c++11copy-constructorassignment-operatorinserter

C++11, const data members, std::inserter, copy


There is a simple example of the class Test

#include <algorithm>
#include <iterator>
#include <vector>

template <typename T>
struct MinMax { T min, max; };

template <typename T>
using TList = std::vector<T>;

template <typename T>
class Test
{
   private:
      const T a, b;         
      const MinMax <T> m;   
   public:
      Test() : a(0), m{ 0, 0 }, b(0.0) {};
   public:
      T getA() const { return a; }
      MinMax <T> & getMinMax() const { return m; }
      T getB() const { return b; }
      Test(const Test &t) : a(t.a), b(t.b), m(t.m ) {}
};

with the constant data members. Instead of the constructor, the data are not changed. I would like to copy the vector of Test objects to another vector using std::inserter. I am surprised that the copy constructor is not sufficient

int main()
{
   TList <Test <double> > t1;
   TList <Test <double> > t2;
   Test<double> t;
   t1.push_back(t);
   std::copy(t2.begin(), t2.end(), std::inserter(t1, t1.begin()));

   return 0;
}

and the following compiler error appears (VS2015):

Error   C2280   'Test<double> &Test<double>::operator =(const Test<double> &)': attempting to reference a deleted function  Const

Is it possible to let the data members const and perform a copy in a different way (some hack :-))? Or an operator = must be defined, so the data members cannot be const (it is imppossible to assign to an object with const data members)?

Thanks for your help.


Solution

  • An insert to a vector reassigns all elements after the inserted element and assigns the inserted element to the freed up slot.

    In other words, you can't because the Standard requires the elements of standard containers to be Asssignable (have a = b defined) to offer full functionality.

    Apart from the obvious solution of writing your own operator= you can also add elements with const members to a vector by pushing back:

    std::copy(t2.begin(), t2.end(), std::back_inserter(t1));
    

    but that's kind of working against the Standard; push_back doesn't happen to need assignability but other functions may.

    Or, you can use a container that doesn't require assignability to insert, e.g.:

    template <typename T>
    using TList = std::list<T>;
    

    trading off all the benefits of contiguous memory cache locality of a vector.

    On a closing note, I tend to avoid declaring data members of my structures const because of such problems with generic containers that require assignability under the hood. Notice that in your example removing const from private members would leave you with good-enough read-only fields (that can be accessed only via getters from outside).