Связанный список, реализованный для типов, созданных по-разному

Я реализовал класс Node, который выглядит следующим образом:

template<unsigned int Size>
class Node
{
    private:
        Eigen::Matrix<float, Size, Size> m_matrix;

        Node<?> *m_previousNode;
        Node<?> *m_nextNode;
};

У него есть переменная-член, размеры которой задаются аргументом шаблона. Кроме того, и это важная часть, он хранит указатели на предыдущий и следующий узел (который может иметь размер, отличный от его собственного размера).

Теперь у меня есть фиксированное количество узлов этого класса разных размеров, которые я хочу сохранить в классе Network. Для начала он может быть трехмерным:

template<unsigned int S0, unsigned int S1, unsigned int S2>
class Network
{
    private:
        Node<S0> *m_firstNode;
        Node<S1> *m_secondNode;
        Node<S2> *m_thirdNode;
};

Вот как я хочу его создать:

Network<10, 20, 5> network;

Как видите, количество узлов фиксировано, как и размер каждого узла; они не могут быть изменены впоследствии.

Мой вопрос в том, как я могу хранить указатели на предыдущий и следующий узел (Node<?> * в приведенном выше коде).

Сначала я подумал о расширении списка аргументов шаблона следующим образом:

template<unsigned int PreviousSize, unsigned int Size, unsigned int NextSize>
class Node
private:
    Eigen::Matrix<float, Size, Size> m_matrix;

    Node<?, PreviousSize, Size> *m_previousNode;
    Node<Size, NextSize, ?> *m_nextNode;

но тогда, очевидно, мне нужно было бы знать размер предшественника предыдущего узла, что привело к той же проблеме — я все еще не могу заполнить ?.

Есть идеи, как это решить?


person wuschelhase    schedule 15.12.2012    source источник
comment
Я иду на риск и предполагаю, что вы, вероятно, не хотите делать то, что, как вы думаете, хотите делать. Шаблоны строятся во время компиляции и не поддаются легкой проверке во время выполнения. Несмотря на то, что вы можете создать какой-нибудь гаджет типа tagged-union или хранить пустые указатели, вам все равно придется работать с результатом во время выполнения, что нецелесообразно.   -  person Kerrek SB    schedule 16.12.2012
comment
Какой структурой данных управляет этот параметр Size? И вы говорите, что размер варьируется от узла к узлу, это не константа для всех узлов в списке?   -  person John Kugelman    schedule 16.12.2012
comment
Возможно, термин «связанный список» вводит в заблуждение. Я хочу настроить список с фиксированным количеством узлов, каждый из которых имеет постоянный размер (константа для каждого узла, каждый из которых может иметь другое значение размера). Количество узлов и их размеры известны во время компиляции.   -  person wuschelhase    schedule 16.12.2012
comment
@wuschelhase: Это действительно звучит совершенно по-другому. Не могли бы вы отредактировать свой вопрос и уточнить свою цель, тогда мы все сможем присоединиться. Если все известно во время компиляции, безусловно, представляется возможным использовать конструкции времени компиляции. Вероятно, с вариативными шаблонами или с использованием минус-списков, чтобы не потеряться.   -  person Matthieu M.    schedule 16.12.2012
comment
@MatthieuM.: Хорошо, надеюсь, кто-нибудь укажет мне правильное направление после того, как я обновлю вопрос. Спасибо!   -  person wuschelhase    schedule 16.12.2012
comment
@MatthieuM.: Теперь я перефразировал вопрос для ясности.   -  person wuschelhase    schedule 16.12.2012
comment
Я думаю, что эта структура данных хорошо известна, как wtf? структура данных. На всякий случай, если это не тот, не могли бы вы нарисовать его и объяснить на этом уровне?   -  person Cheers and hth. - Alf    schedule 16.12.2012


Ответы (1)


Я вижу несколько решений, связанных со связанными списками, но все они, боюсь, уродливы;)

Однако, учитывая, что все узлы списка принадлежат общему объекту Network, то есть, я думаю, наша волшебная карта. Если мы откажемся от идеи списка и вместо этого нацелимся на «позиционированный» узел в сети, тогда все станет намного проще!

template <typename Network, unsigned Index, unsigned Size>
class Node {
public:

private:
    Network* m_network;
    Eigen::Matrix<float, Size, Size> m_matrix;
}; // class Node

И сеть:

template <unsigned Size0, unsigned Size1, unsigned Size2>
class Network {
public:
    template <unsigned Index>
    auto access() -> decltype(m_nodes.get<Index>()) {
        return m_nodes.get<Index>();
    }

    template <unsigned Index>
    auto get() const -> decltype(m_nodes.get<Index>()) {
        return m_nodes.get<Index>();
    }

private:
    std::tuple< Node<Network, 0u, Size0>,
                Node<Network, 1u, Size1>,
                Node<Network, 2u, Size2>> m_nodes;
};

И, наконец, итерация?

template <typename Network, unsigned Index, unsigned Size>
auto Node<Network, Index, Size>::prev() -> decltype(m_network->access<Index-1>()) {
    return m_network->access<Index-1>();
}

template <typename Network, unsigned Index, unsigned Size>
auto Node<Network, Index, Size>::next() -> decltype(m_network->access<Index+1>()) {
    return m_network->access<Index+1>();
}

Ну, за исключением того, что у нас здесь есть небольшая проблема с курицей и яйцом ... мы можем обмануть наш выход, вложив определение Node внутри класса Network. И я мог бы сделать именно это, однако, почему бы просто не согласиться с тем, что итерация всегда должна начинаться с сетевого класса?

В итоге вот что я предлагаю:

template <unsigned Size>
class Node {
public:
    // ...
private:
    Eigen::Matrix<float, Size, Size> m_matrix;
};

template <unsigned Size>
std::ostream& operator<<(std::ostream& out, Node<Size> const&) {
    return out << "Node<" << Size << ">";
}

template <unsigned S, unsigned... Sizes>
class Network {
private:
    // Hack for gcc, using m_nodes in decltype requires that it's already been declared
    typedef std::tuple< Node<S>, Node<Sizes>... > Nodes;
    Nodes m_nodes;

public:

    static constexpr unsigned Size() { return sizeof...(Sizes) + 1; }

    template <unsigned Index>
    auto access() -> decltype(std::get<Index>(this->m_nodes)) {
        return std::get<Index>(this->m_nodes);
    }

    template <unsigned Index>
    auto get() const -> decltype(std::get<Index>(this->m_nodes)) {
        return std::get<Index>(this->m_nodes);
    }

}; // class Network

Конечно, Node больше не знает своего положения, но вы можете заключить его в итератор:

template <typename Network, unsigned Index>
class NetworkIterator {
private:
    // Hack for gcc, using m_network in decltype requires that it's already been declared
    Network& m_network;

public:
    static_assert(Index < Network::Size(), "Index cannot exceed network size by more than one");

    NetworkIterator(Network& n): m_network(n) {}

    auto element() -> decltype(this->m_network.template access<Index>()) {
        return m_network.template access<Index>();
    }

    template <unsigned U = Index - 1>
    NetworkIterator<Network, U> prev() {
       return NetworkIterator<Network, U>(m_network);
    }

    template <unsigned U = Index + 1>
    NetworkIterator<Network, U> next() {
       return NetworkIterator<Network, U>(m_network);
    }
}; // class NetworkIterator

И да, это работает.

person Matthieu M.    schedule 16.12.2012