Как операторы преобразования работают на узле, который имеет указатель на другой узел? - PullRequest
0 голосов
/ 18 апреля 2020
#include <iostream>

template <typename T>
struct Node
{
    T value;
    Node<T>* next;

    // will this recurse till the end of the list?

    operator Node<const T>()
    {
        std::cout << "casted" << std::endl;
        return Node<const T>{value, (Node<const T>*)next};
    }
};

template <typename T>
struct X
{
    Node<T>* node;

    X(Node<T>* node) : node(node) {}

    X<const T> getConst() const
    {
        return X<const T>((Node<const T>*)node);
    }
};

int main()
{
    Node<int> node3{ 0, nullptr };
    Node<int> node2{ 0, &node3  };
    Node<int> node1{ 0, &node2  };

    X<int> x(&node1);

    auto constX = x.getConst();

    std::cout << constX.node->value << std::endl;
}

Вывод:

0

Здесь у меня возникла проблема, когда я хотел привести Node<T> к Node<const T>. В операторе преобразования я приведу указатель next к Node<const T>*. Я думал, что это может повториться до конца списка. Поэтому я попытался создать список и передать его по номеру X и вызвать getConst, чтобы узнать, будет ли напечатанное сообщение напечатано только один или три раза. Удивительно, но оно не напечатало ни одного сообщения. Как работает оператор конвертации? Будет ли он повторяться в этом примере до конца списка? И почему он не печатает никаких сообщений?

1 Ответ

1 голос
/ 18 апреля 2020

Оператор преобразования Node<T>::operator Node<T const>() преобразует объект типа Node<T> в объект типа Node<T const>. Приведение (Node<T const>*)next в этой функции и (Node<T const>*)node в X не являются преобразованиями между Node объектами - они являются преобразованиями между указателями. Все, что происходит, это то, что адрес памяти объекта Node<T> интерпретируется как адрес памяти Node<T const>. В обоих случаях, однако, просто не a Node<T const> по этому адресу памяти (если T уже const), поэтому указатели, полученные в результате приведений, являются «недействительными». Единственное, что вы можете сделать с полученными указателями, это привести их обратно к Node<T>* (и даже это не гарантирует работу).

Ваш код a) никогда не вызывает определенный вами оператор преобразования и b) сломано. Результат x.getConst() содержит недопустимый указатель, а доступ к *constX.node является неопределенным поведением. Кроме того, если что-нибудь вызовет оператор преобразования, это создаст больше недействительных указателей. Если вы действительно хотите X<T const>, то вы действительно должны скопировать все Node s (и сделать это рекурсивно проще всего).

template<typename T>
Node<T>::operator Node<T const>() {
    return {value, next ? new Node<T const>(*next) : nullptr};
    //                        ^^^^^^^^^^^^^^^^^^^^ recursive call
}

template<typename T>
X<T const> X<T>::getConst() const {
    return {new Node<T const>(*node)}; // I would suggest marking the constructor of X explicit
}

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

...