Используйте std:: experimental:: optional для реализации списка

Мне было интересно, возможно ли реализовать единственный (и возможный двойной) связанный список, используя std::experimental::optional.

template <typename T>
struct node {
    std::experimental::optional<node<T>> next;
    T data;
};

Каковы преимущества/недостатки такого дизайна? Могут ли новые функции c++1z использоваться для реализации стражей или избавления от них alltogether? Будет ли это увеличение до n-арных деревьев?

Ответы

Ответ 1

Невозможно реализовать связанный список таким образом, потому что ваш тип node всегда будет неполным. Вот более полный пример, который иллюстрирует проблему:

#include <iostream>
#include <experimental/optional>

template <typename T>
struct node {
    std::experimental::optional<node<T>> next;
    T data;
};

int main( int, char ** )
{
    std::cout << sizeof( node<int> ) << std::endl;
    return 0;
}

Дело в том, что optional<T> требует завершения T, но в точке, где вы определяете next, node является неполным. Причина, по которой optional<T> требует полного типа, заключается в том, что он хранит T непосредственно внутри объекта optional, то есть не выделяет память в куче. В результате он должен знать размер T. Внутри он содержит буфер sizeof( T ). Что касается макета памяти, вы можете думать о optional<T> как

template <class T>
struct optional
{
    bool _containsValue;
    char _buffer[ sizeof( T ) ];
};

но на практике это сложнее из-за требований к выравниванию памяти.

В вашем случае, чтобы узнать размер optional<node>, он должен знать размер node, и для этого он должен знать размер optional<node>.

Ответ 2

Это невозможно, потому что для optional<T> требуется T быть полным.

Согласно N3672 (предложение для std::optional):

Необязательный шаблон шаблона накладывает небольшие требования на T: он должен быть либо ссылочным типом lvalue, либо полным типом объекта, удовлетворяющим требованиям Destructible.