【发布时间】:2020-05-26 14:26:34
【问题描述】:
假设我有以下课程
template <typename T>
struct Node { T value; Node* next; };
通常需要编写与此类似的代码(假设 Sometype 现在是 std::string,尽管我认为这并不重要)。
Node<SomeType> node = Node{ someValue, someNodePtr };
...
Node <const SomeType> constNode = node; // compile error
一种解决方法是定义显式转换运算符:
template <typename T>
struct Node
{
T value;
Node* next;
operator Node<const T>() const {
return Node<const T>{value, reinterpret_cast<Node<const T>* >(next)};
}
};
有没有更好、“正确”的方法来做到这一点? 1. 一般来说,除了显式定义转换运算符之外,允许将 SomeType 转换为 SomeType 的正确方法是什么? (仅在我的示例中没有)。 2.如果需要定义转换运算符, reinterpret_cast 是正确的方法吗?还是有“更清洁”的方式?
编辑:答案和 cmets 非常有帮助。我决定现在提供更多背景信息。我的问题不在于实现 const_iterator 本身(我认为我知道该怎么做),而是如何为迭代器和 const_iterator 使用相同的模板。这就是我的意思
template <typename T>
struct iterator
{
iterator(Node<T>* _node) : node{ _node } {}
T& operator*() { return node->value; } // for iterator only
const T& operator*() const { return node->value; } // we need both for iterator
// for const iterator to be usable
iterator& operator++() { node = node->next; return *this; }
iterator operator++(int) { auto result = iterator{ node }; node = node->next; return result; }
bool operator==(const iterator& other) { return node == other.node; }
bool operator!=(const iterator& other) { return Node != other.node; }
private:
Node<T>* node;
};
实现const_iterator本质上是一样的,只是T& operator*() { return node->value; }。
最初的解决方案是编写两个包装类,一个带有 T& operator*(),另一个没有。或者使用继承,迭代器从 const_iterator 派生(这可能是一个很好的解决方案并且有一个优势——我们不需要为迭代器重写比较运算符,并且可以将迭代器与 const_iterator 进行比较——这通常是有意义的——因为我们检查它们都指向同一个节点)。
但是,我很好奇如何在不继承或两次键入相同代码的情况下编写此代码。基本上,我认为需要一些条件模板生成 - 让方法 T& operator*() { return node->value; } 只为迭代器而不是 const_iterator 生成。正确的方法是什么?如果 const_iterator 将 Node* 视为 Node*,它几乎解决了我的问题。
【问题讨论】:
-
您的重新解释转换是未定义的行为,因为它破坏了严格的别名。
-
怎么会出现这种情况?它通常不应该发生。
Node<SomeType>和Node <const SomeType>是两个不相关的类型,它们不妨命名为P和T。 -
为什么需要
Node<const T>而不是const Node<T>?你有一个用例吗? -
@Nelfeal - 例如实现 const_iterator。
-
或者,如果我编写了一个智能指针版本,并且我希望允许将 SmartPointer
转换为 SmartPointer ,因为 T* 到 const T* 是有效的转换。
标签: c++ template-argument-deduction