简体   繁体   English

自定义容器列表:从迭代器编译到 const_iterator 的问题

[英]Custom container list: problem with compiling from iterator to const_iterator

I am trying to recreate some of the C++ containers for a school project and for that I had to also implement iterators.我正在尝试为学校项目重新创建一些 C++ 容器,为此我还必须实现迭代器。 I am currently working on the List container and I am facing a conversion problem.我目前正在处理 List 容器,并且遇到了转换问题。 Here are the parts of the code that are involved:以下是涉及的代码部分:

  • I have an Elem structure (corresponding to 1 element of a doubly linked list that I use for my List container)我有一个 Elem 结构(对应于我用于 List 容器的双向链表的 1 个元素)
 template <class T> struct Elem { Elem *prev; T data; Elem *next; };
  • a BidirectionalIterator class (used for the list iterators).双向迭代器 class(用于列表迭代器)。 Here are the constructors:以下是构造函数:
 template <class T> class BidirectionalIterator { public: typedef BidirectionalIterator iterator; typedef T value_type; typedef size_t size_type; BidirectionalIterator() { _ptr = nullptr; }; BidirectionalIterator(Elem<value_type> *ptr) { *this->_ptr = ptr; }; BidirectionalIterator(const iterator &x) { *this->_ptr = x._ptr; }; ~BidirectionalIterator() {}; iterator &operator=(const iterator &x) { *this->_ptr = x._ptr; return (*this); }; [...] };
  • and my list class:和我的清单 class:
 template <class T, class Alloc = std::allocator<T>> class list { public: typedef T value_type; typedef BidirectionalIterator<T> iterator; typedef BidirectionalIterator<const T> const_iterator; typedef size_t size_type; /* CONSTRUCTORS */ [...] list(const list &x) { _init_list(); assign(x.begin(), x.end()); }; /* ITERATORS */ iterator begin() { return (iterator(_start)); }; const_iterator begin() const { return (const_iterator(_start)); }; iterator end() { return (iterator(_tail)); }; const_iterator end() const { return (const_iterator(_tail)); }; /* ASSIGN */ void assign(iterator first, iterator last); void assign(const_iterator first, const_iterator last); [...] private: Elem<value_type> *_head; Elem<value_type> *_start; Elem<value_type> *_end; Elem<value_type> *_tail; [...] };

In my main program I' m just calling a function (T being an int) that implicitely calls the copy constructor:在我的主程序中,我只是调用一个隐式调用复制构造函数的 function(T 是一个 int):

void print_content(ft::list<T> lst);

But when I compile i get this:但是当我编译时,我得到了这个:

./List.hpp:71:12: error: no matching conversion for functional-style cast from 'Elem<ft::list<int, std::allocator<int>
      >::value_type> *const' (aka 'Elem<int> *const') to 'ft::list<int, std::allocator<int> >::const_iterator' (aka
      'BidirectionalIterator<const int>')
                        return (const_iterator(_start));
                                ^~~~~~~~~~~~~~~~~~~~~
./List.hpp:53:13: note: in instantiation of member function 'ft::list<int, std::allocator<int> >::begin' requested
      here
                        assign(x.begin(), x.end());
./../Iterator/BidirectionalIterator.hpp:45:3: note: candidate constructor not viable: no known conversion from
      'Elem<ft::list<int, std::allocator<int> >::value_type> *const' (aka 'Elem<int> *const') to
      'Elem<ft::BidirectionalIterator<const int>::value_type> *' (aka 'Elem<const int> *') for 1st argument
                BidirectionalIterator(Elem<value_type> *ptr) {

I don't know how to fix that problem.我不知道如何解决这个问题。 I already tried to delete the const attribute from my copy constructor and it works, but it needs to be const (for the rest of my project cause I'm implementing the relational operators that call a const list, and also to respect the original container constructor).我已经尝试从我的复制构造函数中删除 const 属性并且它可以工作,但它需要是 const (对于我项目的 rest,因为我正在实现调用 const 列表的关系运算符,并且还要尊重原始容器构造函数)。

Does anyone have an idea?有人有想法吗?

You try to create an Elem<const int>* from an Elem<int> *const .您尝试从Elem<int> *const创建Elem<const int>*

I suggest making the iterator's pointer Elem<std::remove_const_t<T>>* (even for a const_iterator ) but let dereferencing a const_iterator return a T const& or T const * .我建议制作迭代器的指针Elem<std::remove_const_t<T>>* (即使对于const_iterator )但让取消引用const_iterator返回T const&T const *

Example:例子:

template <class T>
class BidirectionalIterator {
public:
    using value_type = T;
    using reference = value_type&;
    using pointer = value_type*;
    using size_type = std::size_t;

    BidirectionalIterator() : _ptr(nullptr) {};
    BidirectionalIterator(Elem<std::remove_const_t<value_type>>* ptr) : _ptr(ptr) {};
    BidirectionalIterator(const BidirectionalIterator& x) {
        _ptr = x._ptr;
    };
    BidirectionalIterator& operator=(const BidirectionalIterator& x) {
        _ptr = x._ptr; 
        return *this;
    };

    reference operator*() const { return _ptr->data; }
    pointer operator->() const { return &_ptr->data; }

    Elem<std::remove_const_t<value_type>>* _ptr;
};

A slightly better version to let you create list s of const T s and to also let you convert iterator s to const_iterator s (but not the other way around) to be able to compare iterators could look like this:一个稍微好一点的版本可以让您创建const Tlist并且还可以让您将iterator转换为const_iterator (但不能反过来)以便能够比较迭代器,如下所示:

#include <memory>
#include <type_traits>

template <class T, class ElemType> // const or non-const T and the type used in Elem
class BidirectionalIterator {
public:
    using value_type = T;
    using reference = value_type&;
    using pointer = value_type*;
    using size_type = std::size_t;

    BidirectionalIterator() : _ptr(nullptr) {};
    BidirectionalIterator(Elem<ElemType>* ptr) : _ptr(ptr) {};

    // let a conversion constructor of the const_iterator read _ptr
    friend class BidirectionalIterator<const ElemType, ElemType>;

    // enable a const_iterator to be created from a non-const iterator via
    // a conversion constructor
    template<typename U = T, typename V = ElemType,
        std::enable_if_t<std::is_const_v<U>&&!std::is_const_v<V>, int> = 0
    >
    BidirectionalIterator(const BidirectionalIterator<ElemType, ElemType>& x) :
        _ptr(x._ptr) {}

    // normal copy ctor 
    BidirectionalIterator(const BidirectionalIterator& x) : _ptr(x._ptr) {}

    BidirectionalIterator& operator=(const BidirectionalIterator& x) {
        _ptr = x._ptr; 
        return *this;
    };

    // the conversion constructor lets you compare a const_iterator and an iterator
    bool operator==(const BidirectionalIterator& rhs) const { 
        return _ptr == rhs._ptr;
    }
    bool operator!=(const BidirectionalIterator& rhs) const {
        return !(_ptr == rhs._ptr); 
    }
    
    reference operator*() const { return _ptr->data; }
    pointer operator->() const { return &_ptr->data; }

private:
    Elem<ElemType>* _ptr;
};

// iterator == const_iterator, swap order to use member operator==
template<typename T>
bool operator==(const BidirectionalIterator<T, T>& a,
                const BidirectionalIterator<const T, T>& b) {
    return b == a;
}

// iterator != const_iterator, swap order to use member operator!=
template<typename T>
bool operator!=(const BidirectionalIterator<T, T>& a,
                const BidirectionalIterator<const T, T>& b) {
    return b != a;
}

With this iterator definition, you'd need to define your iterator and const_iterator slightly different.使用此迭代器定义,您需要定义iteratorconst_iterator略有不同。

template <class T, class Alloc = std::allocator<T>>
class list {
public:
    using value_type = T;
    using iterator = BidirectionalIterator<T, T>; 
    using const_iterator = BidirectionalIterator<const T, T>;
    //...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM