I am implementing a doubly linked list which has sentinel nodes as its head and tail, say this class named List
. Node
is a private structure in List
. This class has a private method Init
for initializing the head and tail node, which is invoked in the constructors of List
.
template<typename T>
class List {
public:
List() {
Init();
}
...
private:
struct Node {
T data;
Node* prev;
Node* next;
// Constructors
};
size_t size;
Node* head;
Node* tail;
void Init() {
// Codes arise some problem if instances of T have no default constructor.
head = new Node;
tail = new Node;
head->prev = nullptr;
head->next = tail;
tail->prev = head;
tail->next = nullptr;
size = 0;
}
};
Now, the question is that, if instances of T
have no default constructor, I cannot create sentinel nodes using head = new Node;
and tail = new Node;
. The new
operator always allocates a piece of memory and constructs it. When constructing the Node
object, it must use some constructor of T
to initialize the data
field in Node
.
Is there a way to inspect which constructors (except the copy and move construtors) of T
I can use to construct the variable data
of type T
? Or can I only initialize the prev
and next
fields in Node
, leaving the data
field uninitialized?
Your code has a type T that should follow the concepts you are requiring.
In your case, you want it to be default constructible. If the instantiator of the template doesn't provide a T the complies, it'll get a large template error.
You could add a static_assert to your code, to provide a better message (and to provide it earlier, if your method ain't directly used)
You could use: static_assert(is_default_constructible_v)
.
More variants exist, if it shouldn't throw ..., see documentation
In C++20, you could use concepts as well to restrict T. I'm not fluent in them, so I'll leave it to you to find the correct syntax.
There are other solution, like storing std::optional, or using variadic arguments on your init function in order to construct T. In which case you might want to restrict the code using std::is_constructible. std::aligned_storage could also work if you don't mind placement news. All depends on what you want to achieve with this.