Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/127.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如何将模板参数T转换为常量T?_C++_Template Argument Deduction - Fatal编程技术网

C++ 如何将模板参数T转换为常量T?

C++ 如何将模板参数T转换为常量T?,c++,template-argument-deduction,C++,Template Argument Deduction,假设我有一个下面的类 template <typename T> struct Node { T value; Node* next; }; 模板 结构节点{T值;节点*next;}; 通常需要编写类似的代码(现在假设Sometype是std::string,尽管我认为这并不重要) Node Node=Node{someValue,someNodePtr}; ... Node constNode=Node;//编译错误 解决方法之一是定义显式转换运算符: template &l

假设我有一个下面的类

template <typename T>
struct Node { T value; Node* next; };
模板
结构节点{T值;节点*next;};
通常需要编写类似的代码(现在假设Sometype是std::string,尽管我认为这并不重要)

Node Node=Node{someValue,someNodePtr};
...
Node constNode=Node;//编译错误
解决方法之一是定义显式转换运算符:

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)};
    }
};
模板
结构体类型
{
T值;
节点*下一步;
运算符节点()常量{
返回节点{value,reinterpret_cast(next)};
}
};
有没有更好的“适当”的方法? 1.一般来说,除了显式定义转换运算符外,允许将SomeType转换为SomeType的正确方法是什么?(不仅仅在我的例子中)。 2.如果定义转换运算符是必要的,那么重新解释是正确的方法吗?还是有“更干净”的方法

编辑:答案和评论非常有用。我决定现在就提供更多的上下文。我的问题不是实现const_迭代器本身(我认为我知道如何实现),而是如何为迭代器和const_迭代器使用相同的模板。这就是我的意思

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;
};
模板
结构迭代器
{
迭代器(Node*\u Node):Node{{u Node}{}
T运算符*(){return node->value;}//仅用于迭代器(&O)
const T&operator*()const{return node->value;}//迭代器需要这两者
//使常量迭代器可用
迭代器和运算符++(){node=node->next;返回*this;}
迭代器运算符++(int){auto result=iterator{node};node=node->next;返回结果;}
布尔运算符==(常量迭代器和其他){return node==other.node;}
布尔运算符!=(常量迭代器和其他){return Node!=other.Node;}
私人:
节点*节点;
};
实现const_迭代器本质上是相同的,除了T&operator*(){return node->value;}

最初的解决方案是只编写两个包装器类,一个带有T&运算符*(),另一个没有。或者使用继承,迭代器派生自const_iterator(这可能是一个很好的解决方案,并且有一个优势-我们不需要为迭代器重写比较运算符,并且可以将迭代器与const_iterator进行比较-这通常是有意义的-因为我们检查它们是否都指向同一节点)


然而,我很好奇如何在不继承或不输入两次相同代码的情况下编写此代码。基本上,我认为需要一些条件模板生成——只为迭代器而不是常量迭代器生成方法T&operator*(){return node->value;}。正确的方法是什么?如果const_iterator将节点*视为节点*,它几乎解决了我的问题。

也许您需要另一个类,如下所示:

template<typename T>
struct NodeView
{
    T const& value; // Reference or not (if you can make a copy)
    Node<T>* next;

    NodeView(Node<T> const& node) :
    value(node.value), next(node.next) {
    }
};
template<typename T>
struct iterator_base {
    using reference = T&;
    using node_pointer = Node<T>*;
};

template<typename T>
struct const_iterator_base {
    using reference = T const&;
    using node_pointer = Node<T> const*;
};

template<typename T, bool is_const>
using select_iterator_base = std::conditional_t<is_const, const_iterator_base<T>, iterator_base<T>>;

有没有更好的“适当”的方法

<> P>既然你的解决方案都有怪异行为,而且也被C++标准规定的无效。 有一个称为严格别名的规则,它规定了哪种指针类型可以别名另一种类型。例如,
char*
std::byte*
都可以别名任何类型,因此此代码有效:

struct A {
    // ... whatever
};

int main() {
    A a{};
    std::string b;

    char* aptr = static_cast<void*>(&a);          // roughtly equivalent to reinterpret
    std::byte* bptr = reintepret_cast<std::byte*>(&b); // static cast to void works too
}
如您所见,只复制一个数据,而不复制其余节点


你能做什么

在评论中,您提到您想要实现一个常量迭代器。这可以在不更改数据结构的情况下完成:

// inside list's scope
struct list_const_iterator {

    auto operator*() -> T const& {
        return node->value;
    }

    auto operator++() -> node_const_iterator& {
        node = node->next;
        return *this;
    }

private:
    Node const* node;
};
由于包含指向常量节点的指针,因此无法在节点内部更改
值。表达式
node->value
产生一个
T常量&

由于这些节点只是为了实现
列表
,因此我假设它们被完全抽象掉了,并且从未向列表的用户公开过

如果是这样,那么您永远不必转换节点,并且在列表及其迭代器的实现中对指向常量的指针进行操作

要重用同一迭代器,我将执行以下操作:

template<typename T>
struct NodeView
{
    T const& value; // Reference or not (if you can make a copy)
    Node<T>* next;

    NodeView(Node<T> const& node) :
    value(node.value), next(node.next) {
    }
};
template<typename T>
struct iterator_base {
    using reference = T&;
    using node_pointer = Node<T>*;
};

template<typename T>
struct const_iterator_base {
    using reference = T const&;
    using node_pointer = Node<T> const*;
};

template<typename T, bool is_const>
using select_iterator_base = std::conditional_t<is_const, const_iterator_base<T>, iterator_base<T>>;
模板
结构迭代器{
使用reference=T&;
使用node_指针=node*;
};
模板
结构常量迭代器基{
使用reference=T const&;
使用node_指针=node const*;
};
模板
使用select_迭代器_base=std::conditional\u t;
然后简单地用布尔值参数化迭代器类型:

template<bool is_const>
struct list_basic_iterator : select_iterator_base<is_const> {

    auto operator*() -> typename select_iterator_base<is_const>::reference {
        return node->value;
    }

    auto operator++() -> list_basic_iterator& {
        node = node->next;
        return *this;
    }

private:
    typename select_iterator_base<is_const>::node_ptr node;
};

using iterator = list_basic_iterator<false>;
using const_iterator = list_basic_iterator<true>;
模板
结构列表\u基本\u迭代器:选择\u迭代器\u基{
自动运算符*()->typename选择迭代器基::引用{
返回节点->值;
}
auto operator++()->list_basic_iterator&{
节点=节点->下一步;
归还*这个;
}
私人:
typename选择\迭代器\基::节点\ ptr节点;
};
使用迭代器=列表\基本\迭代器;
使用常量迭代器=列表基本迭代器;

您的重新解释强制转换是未定义的行为,因为它打破了严格的别名。这种情况是怎么发生的?它通常不会发生
Node
Node
是两种不相关的类型,它们也可以命名为
P
T
。为什么需要
节点而不是
常量节点
?您是否考虑了一个用例?@Nelfeal-例如实现const_迭代器。或者,如果我编写了一个版本的智能指针,并且希望允许将SmartPointer转换为SmartPointer,因为T*转换为const T*是一个有效的转换。我认为应该使用pointer=std::conditional_T和Reference=std::conditional_T。我认为T&const不是有效的类型。@RazielMagius
const T*
T const*
是相同的
T*const
是一个常量指针
T&const
确实是无效的,因为引用总是常量。@RazielMagius您感到困惑。看看这些错误
const
限定左边的对象,除非左边没有任何内容,因为它位于类型的开头,在这种情况下,它限定右边的对象
// inside list's scope
struct list_const_iterator {

    auto operator*() -> T const& {
        return node->value;
    }

    auto operator++() -> node_const_iterator& {
        node = node->next;
        return *this;
    }

private:
    Node const* node;
};
template<typename T>
struct iterator_base {
    using reference = T&;
    using node_pointer = Node<T>*;
};

template<typename T>
struct const_iterator_base {
    using reference = T const&;
    using node_pointer = Node<T> const*;
};

template<typename T, bool is_const>
using select_iterator_base = std::conditional_t<is_const, const_iterator_base<T>, iterator_base<T>>;
template<bool is_const>
struct list_basic_iterator : select_iterator_base<is_const> {

    auto operator*() -> typename select_iterator_base<is_const>::reference {
        return node->value;
    }

    auto operator++() -> list_basic_iterator& {
        node = node->next;
        return *this;
    }

private:
    typename select_iterator_base<is_const>::node_ptr node;
};

using iterator = list_basic_iterator<false>;
using const_iterator = list_basic_iterator<true>;