Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is storing objects of class with overloaded "operator&" in STL containers legal in C++?

According to C++03 Standard (23.1/3) only copy-constructible class objects can be stored in STL containers. Copy-constructible is described in 20.1.3 and requires that "&" yields address of the object.

Now suppose I have this class:

class Class {
public:
   Class* operator&()
   {
       //do some logging
       return this;
   }
   const Class* operator&() const
   {
       //do some logging
       return this;
   }
   //whatever else - assume it doesn't violate requierements
};

Can this class objects be legally stored in STL containers?

like image 836
sharptooth Avatar asked Dec 28 '11 10:12

sharptooth


2 Answers

Yes. In C++03, the CopyConstructible requirements for &, given values t of type T and u of type const T, are:

  • &t has type T*, and gives the address of t, and
  • &u has type const T*, and gives the address of u.

Your overloaded operators have this behaviour; so, assuming the class meets the other CopyConstructible and Assignable requirements, values of this type can be stored in any C++03 container.

C++11 relaxes these requirements, requiring that types be movable or copyable only in containers or operations that specifically have such requirements, and removing the rather odd specification of what & must do; so your class is still fine, again assuming it meets all the other requirements for the particular container and set of operations you use.

like image 196
Mike Seymour Avatar answered Oct 04 '22 21:10

Mike Seymour


While in C++03 we only had the possibility to copy construct / copy assign objects, we get two additional, by far more powerful tools to construct objects and one additional tool to assign objects: move construction and assignment and emplace construction (based on perfect forwarding).

Thanks to that, the standard gives the value_type of a container way more leeway in its requirements. For example, you are allowed to store unique_ptrs, which are move-only, in a std::vector, as long as you don't use any operations that require CopyConstructible, CopyInsertable or CopyAssignable (such as assignment of one container to another).

Can this class objects be legally stored in STL containers?

Yes, none of the requirements that may be used in a container instantiated with a certain value_type even mention the address-of operator in any way.

§17.6.3.1 [utility.arg.requirements]

The template definitions in the C++ standard library refer to various named requirements whose details are set out in tables 17–24.

(Table 17 and 18 are Comparable requirements)

Table 19 — DefaultConstructible requirements [defaultconstructible]
Expression               Post-condition
T t;                     object t is default-initialized
T u{};                   object u is value-initialized
T()                      a temporary object of type T is value-initialized
T{}

Table 20 — MoveConstructible requirements [moveconstructible]
Expression               Post-condition
T u = rv;                u is equivalent to the value of rv before the construction
T(rv)                    T(rv) is equivalent to the value of rv before the construction
rv’s state is unspecified

Table 21 — CopyConstructible requirements (in addition to MoveConstructible) [copyconstructible]
Expression               Post-condition
T u = v;                 the value of v is unchanged and is equivalent to u
T(v)                     the value of v is unchanged and is equivalent to T(v)

Table 22 — MoveAssignable requirements [moveassignable]
Expression    Return type    Return value    Post-condition
t = rv        T&             t               t is equivalent to the value of
                                             rv before the assignment
rv’s state is unspecified.

Table 23 — CopyAssignable requirements(in addition to MoveAssignable) [copyassignable]
Expression    Return type    Return value    Post-condition
t = v         T&             t               t is equivalent to v, the value of
                                             v is unchanged

Table 24 — Destructible requirements [destructible]
Expression               Post-condition
u.~T()                   All resources owned by u are reclaimed, no exception is propagated.

Then we also got the container requirements themselves. Those are based on their allocator types:

§23.2.1 [container.requirements.general] p13

All of the containers defined in this Clause and in (21.4) except array meet the additional requirements of an allocator-aware container, as described in Table 99. Given a container type X having an allocator_type identical to A and a value_type identical to T and given an lvalue m of type A, a pointer p of type T*, an expression v of type T, and an rvalue rv of type T, the following terms are defined. (If X is not allocator-aware, the terms below are defined as if A were std::allocator<T>.)

  • T is CopyInsertable into X means that the following expression is well-formed: allocator_traits<A>::construct(m, p, v);

  • T is MoveInsertable into X means that the following expression is well-formed: allocator_traits<A>::construct(m, p, rv);

  • T is EmplaceConstructible into X from args, for zero or more arguments args, means that the following expression is well-formed: allocator_traits<A>::construct(m, p, args);

An excerpt from the table for sequence containers (where p is a valid const iterator and t is an lvalue of type T):

a.insert(p,t)
Requires: T shall be CopyInsertable into X. For vector and deque, T shall also be CopyAssignable.
Effects: Inserts a copy of t before p.

If you never use that specific insert variant (and other members, that require CopyInsertible), your type doesn't need to be CopyInsertable. Easy as that. Same goes for all the other members. The only requirement that must be fulfilled is the Destructible requirement (logical, isn't it?).

like image 44
Xeo Avatar answered Oct 04 '22 21:10

Xeo