0
#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;
}

Output:

0

Here I had a problem where I wanted to cast Node<T> to Node<const T>. In the conversion operator, I cast the next pointer to Node<const T>*. I thought this might recurse till the end of the list. So I tried to create a list and pass it to X and call getConst to see whether the message "casted" will be printed only one time or three times. Surprisingly it didn't print any message. How does conversion operator work? Will it recurse in this example till the end of the list? And why isn't it printing any message?

StackExchange123
  • 1,871
  • 9
  • 24
  • One question: do you want to make `Node` objects immutable? – Mister_Jesus Apr 18 '20 at 08:45
  • 1
    [Don't use C-style casts.](https://stackoverflow.com/a/332086/5684257) If you had used a `static_cast`, you'd have been told that your code is wrong. Also, [`std::endl`](https://stackoverflow.com/a/213977/5684257) is usually unnecessary. – HTNW Apr 18 '20 at 09:06
  • @Mister_Jesus I just wanna be able to return a node of ``const T`` so that the value inside it can't be changed. – StackExchange123 Apr 18 '20 at 09:19
  • @HTNW Yes indeed, when I use static_cast it doesn't work. – StackExchange123 Apr 18 '20 at 09:20

1 Answers1

1

The conversion operator Node<T>::operator Node<T const>() converts an object of type Node<T> to an object of type Node<T const>. The cast (Node<T const>*)next in this function and the cast (Node<T const>*)node in X are not conversions between Node objects—they are conversions between pointers. All that happens is that the memory address of the Node<T> object is reinterpreted as the memory address of a Node<T const>. In both cases, though, there simply isn't a Node<T const> at that memory address (unless T is already const), so the pointers resulting from the casts are "invalid". The only thing you can do with the resulting pointers is cast them back to Node<T>* (and even that isn't guaranteed to work).

Your code a) never calls the conversion operator you defined and b) is broken. The result of x.getConst() contains an invalid pointer, and the access to *constX.node is undefined behavior. Further, if anything ever did call the conversion operator, it would create more invalid pointers. If you really want an X<T const>, then you really have to copy all of the Nodes (and doing that recursively is easiest).

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
}

It's better to structure your code so you just don't need to do this expensive conversion in the first place, though.

HTNW
  • 27,182
  • 1
  • 32
  • 60