I know there is an "assignable and copy constructable" requirement for objects in an STL container. For example, you cannot place an object with a const member into a vector.

#include <vector>
    // /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;
    // }

However, I noticed that it works with std::set:

#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;
      return 0;

Will this only work on some compilers? Or is this ok to do?



Well from the link, the assignment operator requirement is only for "output iterators" where the expression *it = a; must be valid. BTW, the iterators for the std::set container are not output iterators. So, that shouldn't be a problem.

But iterators are not all, you need to watch out for what you can do with the container. People often say that the value type of any container has to be CopyAssignable and CopyConstructible. Well, that is an oversimplification of reality. If you want all operations to be possible with a container, then yes, you need to meet those requirements. But the operations that actually require copy-assignable value-types are actual not that many, especially for std::set (whose philosophy is that its key-values are to be constant after the initial insertion). For std::set, the only operation I could find that really require copy-assignable (or move-assignable) values is the assignment operator of the container itself.

This argument goes for the other containers, the C++ standard specifies the requirements on the value-type on a per-function basis on the containers (e.g. if you want to do insert() in a vector container you need a MoveConstructible and MoveAssignable value type, etc.). This makes sense since containers are class templates for which only the functions that are used are actually instantiated, so, the requirements on the value type is just the addition of the requirements of all the functionality of the container that you actually use in your code. Generally, all the "link-list" or "tree" type containers don't require CopyAssignable or MoveAssignable value types for much of anything since they, by design, avoid doing copying as much as possible.

The above are the theoretics. In practice, I'm not sure how fine-grained the requirements on the value types are when it comes to actual implementations of the containers, but I don't see any reason why it would be less fine-grained than what the standard prescribes. Of course, you might have surprises. In my experience, when you use a container as an immutable container of immutable values, it is OK if the value type is const (or non-assignable).