Embedding an array of custom design objects in a C ++ class

C ++ does not allow a class containing an array of elements that are not constructive by default:

class Gordian {
  public:
    int member;
    Gordian(int must_have_variable) : member(must_have_variable) {}
};

class Knot {
  Gordian* pointer_array[8]; // Sure, this works.
  Gordian inlined_array[8]; // Won't compile. Can't be initialized.
};

As even novice C ++ users know, the language ensures that all members that are not members of the POD are initialized when the class is built. And he does not trust the user to initialize everything in the constructor - you need to provide valid arguments to the constructors of all members before the constructor body even starts.

This is usually a great idea, as far as I know, but I came across a situation where it would be much easier if I could have an array of structural objects that are not standard.

: . , . ( ). , , , - .

, , , . , , :

class Knot {
  public:
    struct dummy { char padding[sizeof(Gordian)]; };
    dummy inlined_array[8];
    Gordian* get(int index) {
      return reinterpret_cast<Gordian*>(&inlined_array[index]);
    }
    Knot() {
      for (int x = 0; x != 8; x++) {
        new (get(x)) Gordian(x*x);
      }
    }
};

, , ++. , . , :

1) , ? ? ( ++ 0x GCC).

2) ?

+3
3

, (, boost::array) :

#include <boost/array.hpp>

class Gordian {
public:
    int member;
    Gordian(int must_have_variable) : member(must_have_variable) {}
};

namespace detail
{
    boost::array<Gordian, 8> chop()
    {
        boost::array<Gordian, 8> t = {{0, 1, 4, 9, 16, 25, 36, 49}};
        return t;
    }
}

class Knot {
    boost::array<Gordian, 8> array;
public:
    Knot(): array(detail::chop()) {}
};

boost::optional ( ):

#include <boost/optional.hpp>

class Gordian {
public:
    int member;
    Gordian(int must_have_variable) : member(must_have_variable) {}
};

class Knot {
    boost::optional<Gordian> array[8];
public:
    Knot()
    {
        for (int x = 0; x != 8; x++) {
            array[x] = Gordian(x*x);
        }
    }
};
+1

, Knot , , . , . , , - , , :

SuperOverOptimisedVector<Gordian> v;
...
Gordian* g = v.append();
new (g) Gordian(42);

, . v.append() . ( , , , std::vector) .

, , . , :

new (v.append()) Gordian(42);

, .

, , , , , std::vector.

+1

Based on the answer received and my initial hack, I came up with this general solution using boost :: aligned_storage . Mostly void type, but for structures.

class Gordian {
  public:
    int member;
    Gordian(int must_have_variable) : member(must_have_variable) {}
};

template <class T>
struct VoidWrap {
  boost::aligned_storage<sizeof(T)> storage;
  T* address() { return reinterpret_cast<T*>(storage.address()); }
};

class Knot {
  public:
    VoidWrap<Gordian> void_gordian[8];
    Knot() {
      for (int x = 0; x != 8; x++) {
        new (void_gordian[x].address()) Gordian(x*x);
      }
    }
};

Or an extended version specifically for use case 1) Initialization in the constructor of the object containing it. 2) Access. 3) It is possible to reset the value. 4) Proper automatic destruction. (Of course, it can explode if it is destroyed / accessible without initialization)

template <class T>
struct VoidWrap {
  boost::aligned_storage<sizeof(T)> storage;
  /// Returns an address on which to construct the object. Use only once.
  T* construct() { return access(); }
  /// access should only be called on a `construct`ed object
  /// obj.access() is equivalent to &obj
  T* access() { return reinterpret_cast<T*>(this); }
  /// Assumes the object has been constructed. Calls the destructor on the old
  /// object, then returns an address on which to construct a new object.
  T* reconstruct() {
    access()->~T();
    return access(); 
  }
  ~VoidWrap() {
    access()->~T();
  }
};

VoidWrap<Gordian> void_g;
new (void_g.construct()) Gordian(10);
cout << void_g.access()->member << endl;
new (void_g.reconstruct()) Gordian(20);
+1
source

Source: https://habr.com/ru/post/1742655/


All Articles