Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create dummy object of non-default-constructible class

tl;dr: I want to construct a class ListEntry containing a member of a generic type Value, but Value is not default constructible and ListEntry doesn't know how to construct it. I will never access this Value member, so it doesn't matter that it isn't initialized.

Why I'm doing this

I'm implementing a double linked list roughly similar to the following

template<class Value>
class ListEntry {
  Value value;
  ListEntry<Value> *prev;
  ListEntry<Value> *next;
};
template<class Value>
class List {
  ListEntry<Value> sentinel;
};

The links between the list entries always form a closed circle where the sentinel connects the last list element to the first list element. The sentinel object is initialized with sentinel.prev = &sentinel and sentinel.next = &sentinel.

This way, I get rid of a lot of special cases and I never have to check for nullptr because there are no null pointers. Adding an element to the end of the list (between the last element and the sentinel) is not a special case but the same as adding an element to the middle of the list between two real elements.

So in all real list entries, the value field will contain the actual value of the list entry. For them, I can initialize ListEntry by giving it a Value object in its constructor, so I don't need Value to be default constructible. In the sentinel, the value field will never be accessed. But unfortunately, since Value is not default constructible, the compiler doesn't let me create the sentinel object.

I could make the value member in ListEntry a pointer, boost::optional or something similar. I don't like this due to performance issues though. Any ideas on how to store Value in ListEntry without performance/memory costs and without needing Value to be default constructible? It seems to me like there must be a way of getting a Value object without calling its constructors.

like image 365
Heinzi Avatar asked Apr 26 '15 21:04

Heinzi


2 Answers

Use a raw buffer and placement new :

template<class Value>
class ListEntry {
  alignas(Value) char storage[sizeof(Value)];
  ListEntry<Value> *prev;
  ListEntry<Value> *next;
};

Constructing the Value :

new (entry->storage) Value(/* params */);

Destructing the Value :

reinterpret_cast<Value*>(entry->storage)->~Value();
like image 154
Quentin Avatar answered Sep 27 '22 20:09

Quentin


You can split this into a base class and a node class, e.g.

class ListEntryBase {
    ListEntryBase *prev;
    ListEntryBase *next;
};

template<class Value>
class ListEntry : public ListEntryBase {
    Value value;
};

template<class Value>
class List {
    ListEntryBase sentinel;
};

This way you avoid creating an unneeded value and at the same time Value need not be default constructible.

like image 24
Olaf Dietsche Avatar answered Sep 27 '22 19:09

Olaf Dietsche