Search code examples
c++algorithmdata-structureslinked-listdoubly-linked-list

How to know a generic type T if it has an appropriate constructor for use?


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?


Solution

  • 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.