Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Coding style conformance

...

In this noncompliant code example, a custom container class uses an allocator object to obtain storage for arbitrary element types. While the CopyElementscopy_elements() function is presumed to call copy constructors for elements being moved into the newly-allocated storage, this example fails to explicitly call the default constructor for any additional elements being reserved. If such an element is accessed through the operator[]() function, it results in undefined behavior, depending on the type T.

Code Block
bgColor#FFcccc
langcpp
#include <memory>

template <typename T, typename Alloc = std::allocator<T>>
class Container {
  T *UnderlyingStorageunderlyingStorage;
  size_t NumElementsnumElements;
  
  void CopyElementscopy_elements(T *Fromfrom, T *Toto, size_t Countcount);
  
public:
  void reserve(size_t Countcount) {
    if (Countcount > NumElementsnumElements) {
      Alloc Aalloc;
      T *Pp = Aalloc.allocate(Countcount); // Throws on failure
      try {
        CopyElementscopy_elements(UnderlyingStorageunderlyingStorage, Pp, NumElementsnumElements);
      } catch (...) {
        Aalloc.deallocate(Pp, Countcount);
        throw;
      }
      UnderlyingStorageunderlyingStorage = Pp;
    }
    NumElementsnumElements = Countcount;
  }
  
  T &operator[](size_t Idxidx) { return UnderlyingStorageunderlyingStorage[Idxidx]; }
  const T &operator[](size_t Idxidx) const { return UnderlyingStorageunderlyingStorage[Idxidx]; }
};

Compliant Solution

...

Code Block
bgColor#ccccff
langcpp
#include <memory>

template <typename T, typename Alloc = std::allocator<T>>
class Container {
  T *UnderlyingStorageunderlyingStorage;
  size_t NumElementsnumElements;
  
  void CopyElementscopy_elements(T *Fromfrom, T *Toto, size_t Countcount);
  
public:
  void reserve(size_t Countcount) {
    if (Countcount > NumElementsnumElements) {
      Alloc Aalloc;
      T *Pp = Aalloc.allocate(Countcount); // Throws on failure
      try {
        CopyElementscopy_elements(UnderlyingStorageunderlyingStorage, Pp, NumElementsnumElements);
        for (size_t i = NumElementsnumElements; i < Countcount; ++i) {
          Aalloc.construct(&Pp[i]);
        }
      } catch (...) {
        Aalloc.deallocate(Pp, Countcount);
        throw;
      }
      UnderlyingStorageunderlyingStorage = Pp;
    }
    NumElementsnumElements = Countcount;
  }
  
  T &operator[](size_t Idxidx) { return UnderlyingStorageunderlyingStorage[Idxidx]; }
  const T &operator[](size_t Idxidx) const { return UnderlyingStorageunderlyingStorage[Idxidx]; }
};

Exceptions

MEM53-CPP-EX1: If the object is trivially constructable, it need not have its constructor explicitly called to initiate the object's lifetime. If the object is trivially destructible, it need not have its destructor explicitly called to terminate the object's lifetime. These properties can be tested by calling std::is_trivially_constructible() and std::is_trivially_destructible() from <type_traits>. For instance, integral types such as int and long long do not require an explicit constructor or destructor call.

...