8

See the code below - I am trying to put a const object into a vector. I know the answer is "STL containers require objects to be assignable and copy constructable", but, without citing the standard, can anyone explain what the problem with doing this is? I don't understand why a class like this could not be copied (besides that c++ doesn't allow it).

All it is is a value stored that is not allowed to be changed - why can't putting it in a vector simply create another one of these objects?

#include <vector>

// Attempt 1
// /home/doriad/Test/Test.cxx:3:8: error: non-static const member ‘const int MyClass::x’, can’t use default assignment operator

// struct MyClass
// {
//   int const x;
//   MyClass(int x): x(x) {}
// };
// 
// int main()
// {
//   std::vector<MyClass> vec;
//   vec.push_back(MyClass(3));
//   return 0;
// }

// Attempt 2
// /home/doriad/Test/Test.cxx:28:23: error: assignment of read-only member ‘MyClass::x’
struct MyClass
{
  int const x;
  MyClass(int x): x(x) {}
  MyClass& operator= (const MyClass& other)
  {
    if (this != &other)
    {
      this->x = other.x;
    }

    return *this;
  }
};

int main()
{
  std::vector<MyClass> vec;
  vec.push_back(MyClass(3));
  return 0;
}

EDIT:

It is possible to do this with std::set and std::list. I guess it is the sort() function in std::vector that uses assignment. This is not UB right?

#include <set>

// Attempt 1
struct MyClass
{
  int const x;
  MyClass(int x): x(x) {}
  bool operator< (const MyClass &other) const;
};

bool MyClass::operator<(const MyClass &other) const
{
  if(this->x < other.x)
  {
    return true;
  }
  else if (other.x < this->x)
  {
    return false;
  }

}


int main()
{
  std::set<MyClass> container;
  container.insert(MyClass(3));
  return 0;
}
David Doria
  • 9,873
  • 17
  • 85
  • 147
  • Semi-related: http://stackoverflow.com/questions/2759350/embarassing-c-question-regarding-const/2759426#2759426 – cHao Dec 31 '11 at 00:20
  • Please see my edit using std::set instead of std::vector. Is this ok? – David Doria Dec 31 '11 at 14:06
  • @David : In C++03, using `std::set<>` like so is still ill-formed -- §23.1/3 mandates that element types must be both copy-constructable _and_ assignable. In C++11, your code is well-formed, but your `std::set<>` instance will not be assignable because `MyClass` is not assignable. (Also, only semi-related: your `operator<` implementation is broken, as it will not return any value if `this->x == other.x`). – ildjarn Jan 04 '12 at 19:25
  • ildjarn - you're right about the operator<, just a typo in my post. By "not well-formed", do you mean it could be UB? Or should it not compile? – David Doria Jan 04 '12 at 22:27
  • @David : Code that is "ill-formed" is generally understood to not be compilable, modulo implementation bugs (however technically an implementation is free to compile it as long as it also "diagnoses" the issue, generally by giving a compiler warning). In this case I suspect that any non-trivial use of an instance of `std::set` with a C++03 compiler would break, but it ultimately depends totally on the implementation. – ildjarn Jan 05 '12 at 00:10
  • 1
    Possible duplicate of [Does C++11 allow vector?](https://stackoverflow.com/questions/6954906/does-c11-allow-vectorconst-t) – underscore_d Sep 23 '18 at 11:22

3 Answers3

7

EDIT2: (Removing a bunch of stuff that doesn't have to work) The C++11 standard states that the insert method for vector and deque (and the default implementation of push_back for that matter) requires the value type to be CopyAssignable, i.e., the value supports:

t= v;

Classes and structs with const members are not CopyAssignable by default, so what you want to do won't work.

This doc (n3173) has an explanation for the various container requirements.

MSN
  • 53,214
  • 7
  • 75
  • 105
  • 2
    MSN: then what do I have to do to get it to work? It forced me to implement the assignment operator, but I can't do the assignment because it is const! – David Doria Dec 31 '11 at 00:08
  • 1
    @David : Simply avoid const data members -- they're useless if your class offers proper const-correct getters. – ildjarn Dec 31 '11 at 00:10
  • @David : On the contrary, it is idiomatic -- private non-const data member, public const getter. Any basic C++ book will advise this. – ildjarn Dec 31 '11 at 00:11
  • 1
    Well, you can't have both: a constant data member and an assignable object. After all, the assignment would, indeed, change the constant data member. – Dietmar Kühl Dec 31 '11 at 00:13
  • MSN: I am using gcc 4.6 - that is pretty modern. It doesn't work if I provide a copy constructor and not an assignment operator - same problem as Method 1- it just complains about not having an assignment operator. – David Doria Dec 31 '11 at 00:29
  • 1
    @MSN: The type used in a container must be *Assignable*, and that means that it requires both copy constructor and assignment operator. Whether your compiler is verifying the *Assignable* concept or not is a different issue, but I bet that if you try to insert/remove an element from the middle of a `vector` and the stored type does not provide an assignment operator then your compiler would also fail to compile. – David Rodríguez - dribeas Dec 31 '11 at 01:04
  • @DavidRodríguez-dribeas Ah, yes. I found the relevant section, and it states exactly that for `vector` and `deque`. You could implement `vector` and `deque` to use the copy constructor instead for `insert`, but that's not what the standard says, so you shouldn't. – MSN Dec 31 '11 at 18:08
  • Sorry for digging up an old question, but push_back only requires CopyInsertable, not CopyAssignable (table 101 in §23.2.3[sequence.reqmts]/16) – Cubbi Apr 06 '12 at 16:01
  • @Cubbi, yes, that's true, but the default implementation of `push_back` uses `insert(end(), value)`. – MSN Apr 06 '12 at 16:33
1

One possible solution would be to store pointers to the objects in the vector, because pointers are assignable and copy constructable.

Another possible solution would be to declare x without the const keyword, but ensure that it cannot be modified through encapsulation (i.e. you should declare it as private and don't modify from anywhere outside the constructor)..

WebMonster
  • 2,981
  • 2
  • 22
  • 29
0

When you place an object of type MyClass in the std::vector, the vector will make a copy of the object for storage, and not the object you passed to it.

Eddie Paz
  • 2,219
  • 16
  • 11
  • 1
    Sure, that makes sense. But why can't it copy an object with a const value? It should just be able to make a new object with the same const value, no? – David Doria Dec 31 '11 at 00:10
  • @David : Sure, but you're still missing the 'assignable' requirement. – ildjarn Dec 31 '11 at 00:13