I have a class containing such a structure:
class Store {
private:
struct pointNode {
T data;
std::unique_ptr<pointNode> next;
pointNode():data(std::move(T{})),next(std::move(std::unique_ptr<pointNode>{})){}
pointNode(T n_data, std::unique_ptr<pointNode> ptr) : data(std::move(n_data)), next(std::move(ptr)) {}
};
std::unique_ptr<pointNode> head;
public:
decltype(auto) Begin() ;
};
I need to create a Begin function that will get a pointer to head.
I wrote something like this:
template<typename T>
decltype(auto) Stack<T>::Begin()
{
std::shared_ptr<pointNode> sh_ptr=std::make_shared<pointNode>(head);
return std::move(sh_ptr);
}
That is, create a shared_ptr and return it from the function, then go through the list.
But:
std::shared_ptr<pointNode> sh_ptr=std::make_shared<pointNode>(head);
returns error C2664
Error C2664 "Stack<int>::pointNode::pointNode(Stack<int>::pointNode &&)":
argument 1 cannot be converted from "std::unique_ptr<Stack<int>::pointNode,std::default_delete<Stack<int>::pointNode>>"
in "const Stack<int>::pointNode &"
I tried to remove decltype (auto), but everything is the same .Maybe someone understands what I'm doing wrong .I would be happy to help, thank you
unique_ptr cannot share the data with shared_ptr. That's the point of a unique pointer. But if you want to transfer the ownership, you can do it like so:
#include <string>
#include <memory>
int main()
{
std::unique_ptr<std::string> up( new std::string("Hello, World!\n") );
// up owns the object
// Transfer ownership
std::shared_ptr<std::string> sp( up.release() );
// sp owns the object, up is empty.
}
Having said that, returning a shared_ptr from the begin() function is likely not what you want. I would guess that you want to return a reference or an iterator from that function. Here's how you can return a reference:
template<typename T>
pointNode& Stack<T>::begin()
{
return *head;
}
Note that, for this to work like STL iterators, you need to create a new iterator type. begin returning a refence to a private node class is not a great interface and leaks implementation details.
unique_ptrs claim unique ownership. The clean way to transfer ownership to a shared_ptr would be to move it. So if you have
std::unique_ptr<pointNode> head;
then
std::shared_ptr<pointNode> sh_ptr( std::move(head) );
This is however a design flaw.
You do not want to transfer ownership of the container's elements. Here you actually want to share the raw pointer with the iterator. Any iterator that you have should be able to use that raw pointer - but not destroy the element it points at.
The first draft of a simple forward list type of container would be something like the below - and nowhere should a unique_ptr or shared_ptr be used:
template<class T>
class Store {
private:
struct Node {
T data;
Node* next;
};
Node* head = nullptr;
public:
struct iterator {
iterator(Node* x = nullptr) : current(x) {}
T& operator*() { return current->data; }
T* operator->() { return ¤t->data; }
iterator& operator++() { current = current->next; return *this; }
bool operator!=(const iterator& rhs) const {
return current != rhs.current;
}
private:
Node* current;
};
~Store() {
Node* next;
while(head) {
next = head->next;
delete head;
head = next;
}
}
iterator begin() { return head; }
iterator end() { return nullptr; }
};
Demo
Related
i implemented a custom List including an iterator using the example from a textbook, but when i try to iterate over the list using the iterator, i get the error:
Error C2675 unary '++': 'List::iterator' does not define this
operator or a conversion to a type acceptable to the predefined
operator
does anyone know what's wrong with my class? i'm pretty sure i copied it verbatim from the textbook. thanks!
/*
list class itself - contains links to both ends, size, and many other methods
concrete implementation of List ADT. different from vector because it allows
us to add to middle of list in O(1) time, but does not support O(1) indexing[]
*/
template <typename Object>
class List {
private:
/*
private nested Node class. contains data and pointers to prev/next
along with appropriate constructor methods
struct is a relic from C, essentially a class where members default to public
used to signify a type that contains mostly data to be accessed directly rather
than through methods
Node is private, so only List will be able to access it's fields/methods
*/
struct Node {
Object data;
Node* prev;
Node* next;
Node(const Object& d = Object{}, Node* p = nullptr, Node* n = nullptr) :
data{ d }, prev{ p }, next{ n } {}
Node(Object&& d, Node* p = nullptr, Node* n = nullptr) :
data{ std::move(d) }, prev{ p }, next{ n }
{}
};
public:
/*
const_iterator class. abstracts notion of position, public nested class
const_iterator stores pointer to current node and provides implementation of
basic iterator operations, all in form of overloaded operators like =, ==, !=, ++
*/
class const_iterator {
public:
const_iterator():current{nullptr}{}
const Object& operator*()const
{
return retrieve();
}
const_iterator& operator++()
{
current = current->next;
return *this;
}
const_iterator operator++(int)
{
const_iterator old = *this;
++(*this);
return old;
}
/*
public methods of const_iterator all use operator overloading. operator==,
operator!=, operator* are straightforward. separate routines are necessary for
prefix and postfix versions of operator++ due to their different semantics. we
distinguish between them by their method signatures (empty parameter for prefix,
int parameter for postfix). int is only used to distinguish between them.
in many cases where there is a choice between using prefix or postfix, prefix
version is faster.
*/
bool operator==(const const_iterator& rhs)const
{
return current == rhs.current;
}
bool operator!=(const const_iterator& rhs)const
{
return !(*this == rhs);
}
/*
protected allows classes that inherit from const_iterator to access these fields
but not other classes
*/
protected:
Node* current;
Object& retrieve()const
{
return current->data;
}
const_iterator(Node* p) : current{ p }
{
}
/*
the friend declaration is needed to grant the List class access to const_iterators
nonpublic members
*/
friend class List<Object>;
};
/*
iterator class. abstracts notion of position. public nested class
same functionality as const_iterator, except operator* returns a reference
to the item being viewed, rather than a const reference
iterator can be used in any routine that requires a const_iterator, but not
vice-versa (in other words, iterator IS A const_iterator)
iterator inherits from const_iterator, meaning it inherits all the data and
methods from const_iterator. it can then add new methods and override existing
methods. here we are not adding any new data or changing the behavior of exising
methods. we do add some new methods (with similar signatures to const_iterator)
*/
class iterator : public const_iterator
//inheritance: iterator has same functionality as const_iterator
//iterator can be used wherever const_iterator is needed
{
public:
iterator() {}
/*
do not have to re-implement operator == and operator != (inherited unchanged)
provide a new pair of operator++ implementations that override the original
implementations from const_iterator.
provide an accessor/mutator pair for operator*.
*/
Object& operator*()
{
return const_iterator::retrieve();
}
const Object& operator*()const
{
return const_iterator::operator*();
}
iterator& operator++(int)
{
iterator old = *this;
++(*this);
return old;
}
protected:
/*
protected constructor uses an initialization list to initialize the inherited
current node.
*/
iterator(Node* p) :const_iterator{ p } {}
friend class List<Object>;
};
public:
/*
constructor and big 5. because zero-parameter constructor and copy constructor
must both allocate the header and tail nodes, we provide a init routine.
init creates an empty list.
the destructor reclaims the header and tail nodes, all other nodes, all other
nodes are reclaimed when the destructor invokes clear. similarly, the
copy-constructor is implemented by invoking public methods rather than attempting
low-level pointer manipulations.
*/
List()
{
init();
}
/*
sentinel nodes - makes sense to create an extra node at each end of list to
represent start/end markers. also referred to as header and tail nodes
advantage of sentinel nodes is that it simplifies code by removing special cases
example: without sentinels, removing the first node becomes a special case
because we must reset the list's link to the first node during the remove
operation, and because the remove algorithm in general needs to access the node
prior to the node being removed (and without header, first node does not have
prior)
*/
void init()
{
theSize = 0;
head = new Node;
tail = new Node;
head->next = tail;
tail->prev = head;
}
~List()
{
clear();
delete head;
delete tail;
}
// clear works by repeatedly removing items until the List is empty (uses pop-front)
void clear()
{
while (!empty())
pop_front();
}
List(const List& rhs)
{
init();
for (auto& x : rhs)
{
push_back(x);
}
}
List& operator=(const List& rhs)
{
List copy = rhs;
std::swap(*this, copy);
return *this;
}
List(List&& rhs)
:theSize{ rhs.theSize }, head{ rhs.head }, tail{ rhs.tail }
{
rhs.theSize = 0;
rhs.head = nullptr;
rhs.tail = nullptr;
}
List& operator=(List&& rhs)
{
std::swap(theSize, rhs.theSize);
std::swap(head, rhs.head);
std::swap(tail, rhs.tail);
return *this;
}
// these methods return iterators
iterator begin()
{
return { head->next };
}
const_iterator begin()const
{
return{ head->next };
}
iterator end()
{
return { tail };
}
const_iterator end()const
{
return{ tail };
}
int size()const
{
return theSize;
}
bool empty()const
{
return size() == 0;
}
Object& front()
{
return *begin();
}
const Object& front()const
{
return *begin();
}
Object& back()
{
return *--end();
}
const Object& back() const
{
return *--end();
}
/*
cleverly obtain and use appropriate iterator
insert inserts prior to a position, so push_back inserts prior to the endmarker
pop_back line erase(-end()) creates a temporary iterator corresponding to the
endmarker, retreats the temporary iterator, and uses that iterator to erase.
similar behavior occurs in back
note also that we avoid dealing with node reclamation in pop_front and pop_back
*/
void push_front(const Object& x)
{
insert(begin(), x);
}
void push_front(Object&& x)
{
insert(begin(), std::move(x));
}
void push_back(const Object& x)
{
insert(end(), x);
}
void push_back(Object&& x)
{
insert(end(), std::move(x));
}
void pop_front()
{
erase(begin());
}
void pop_back()
{
erase(--end());
}
/*
inserting a new node between p and p->prev. works by getting a new node
and then changing pointers of p and p-prev in the correct order
also mention usefulness of the sentinels here.
*/
iterator insert(iterator itr, const Object& x)
{
Node* p = itr.current;
theSize++;
Node* newNode = new Node{ x, p->prev,p };
p->prev->next = newNode;
p->prev = newNode;
return newNode;
}
iterator insert(iterator itr, Object&& x)
{
Node* p = itr.current;
theSize++;
p->prev->next = new Node{ std::move(x), p->prev, p };
p->prev = p->prev->next;
return p->prev;
}
/*
erase routines. frst version erases p by rearranging pointers of the nodes just
before and after p, and then returning an iterator representing the item after the
erased element. like insert, erase must also update the size.
second version of erase simply uses an iterator to call the first version of erase,
note - cannot simply use itr++ in 'for' loop and ignore return iterator from erase,
because the value of itr is stale immediately after the call to erase, which is why
erase returns an iterator.
*/
iterator erase(iterator itr)
{
Node* p = itr.current;
iterator retVal{ p->next };
p->prev->next = p->next;
p->next->prev = p->prev;
delete p;
theSize--;
return retVal;
}
iterator erase(iterator from, iterator to)
{
for (iterator itr = from; itr != to;)
{
itr = erase(itr);
}
return to;
}
/*
data members for list - pointers to header and tail nodes. also keeps track of
size in a data member so that the size method can be implemented in constant time
*/
private:
int theSize;
Node* head;
Node* tail;
};
int main()
{
List<int> theList{};
for (int i = 0; i < 10; i++)
theList.push_back(i);
List<int>::iterator iter = theList.begin();
for(int i=0;i<5;i++)
std::cout << *(iter++) << std::endl;
}
This is my LinkedList namespace
namespace LinkedList {
template<class T>
class Node{
public:
Node(const T& data)
:data_(data), next_(nullptr) {}
T data_;
Node<T> *next_;
Node<T> *operator++(){
return next_;
}
};
template<class T>
class LinkedList{
public:
LinkedList()
: head_(nullptr), tail_(nullptr) {}
~LinkedList(){
Node<T> *curr = head_;
Node<T> *next;
while(curr != nullptr){
next = curr->next_;
delete curr;
curr = next;
}
}
void Append(const T& data) {
Node<T> *tmp = new Node<T>(data);
if(head_ == nullptr) {
head_ = tmp;
tail_ = tmp;
} else if(head_ == tail_){
head_->next_ = tmp;
tail_ = tmp;
} else {
tail_->next_ = tmp;
tail_ = tmp;
}
}
void Present(){
for(Node<T> *curr = head_; curr != nullptr; curr=curr->next_){
std::cout << curr->data_ << std::endl;
}
}
Node<T> *begin(){
return head_;
}
Node<T> *end(){
return nullptr;
}
private:
Node<T> *head_;
Node<T> *tail_;
};
}
I was reading into Iterators and wanted to make my LinkedList object compatible with range based for loops. This is what I got from reading about range-based for loops
for(auto x: list){ }
is equivalent to
for(; begin != end; ++begin) { //*begin};
I thought I was being cheeky and could skip a couple steps of operator overloading (!=, *) for my LinkedList to work with range based for loops by programming my iterator into my Node class for no other reason besides that I felt like it. This is only my second day of learning C++ so this might be a stupid question but I am still a little confused on why this doesn't work:
LinkedList::LinkedList<int> list;
list.Append(3);
list.Append(5);
for(auto x: list) { //blah blah blah}
I know that my prefix increment is the core issue, when the for loop does ++__begin, what I am hoping it does is ___begin=_begin->next however it does not. Why is this? How the operator overloading works in my mind is is that whatever member variables I reference in the overloading function, it is referencing from the instance I am operating on. And when I return the ptr, it is setting whatever instance I am operating on to this ptr. I know my understanding of this is wrong because it doesn't work, so please someone explain to me how it actually works lol.
You've written a
template<class T>
Node<T>* Node<T>::operator++();
which would get invoked like
Node<int> n;
Node<int> *p = ++n; // calls the pre-increment operator
Note this is quite different to the canonical form
Node<T>& Node<T>::operator++();
which returns a reference to the incremented object that you can use in an expression.
But there's another problem: operator overloading works
When an operator appears in an expression, and at least one of its operands has a class type or an enumeration type, ...
But Node<T>* is not a class type. It's a pointer, and pointers already have their own built-in operators. You can't overload them. There is no Node<T>*::operator++().
Write an iterator. It's not that bad, and they actually work!
NB. Iterators are designed to generalize pointers, and to have a compatible interface. You can use raw pointers if your container is basically an array - because incrementing a pointer is already the right way to iterate over an array.
It's only when we want to provide array-like or pointer-like access to a non-contiguous structure that we have to do this extra work.
Suppose that I have a following class
template <typename T>
struct Node { T value; Node* next; };
Often one needs to write code similar to this (let's assume that Sometype is std::string for now, although I don't think that it matters).
Node<SomeType> node = Node{ someValue, someNodePtr };
...
Node <const SomeType> constNode = node; // compile error
One way to work around is to define explicit conversion operator:
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)};
}
};
Is there a better, "proper" way to do it?
1. In general, what is the proper way to allow conversion of SomeType to SomeType except explicitly defining conversion operator? (Not in my example only).
2. If defining conversion operator is necessary, is reinterpret_cast is the proper way to do it? Or there are "cleaner" ways?
EDIT: Answers and comments were very helpful. I decided to provide more context right now. My problem is not with implementing const_iterator itself (I think that I know how to do it), but how to use same template for iterator and const_iterator. Here is what I mean
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;
};
Implementing const_iterator is essentially the same, except that T& operator*() { return node->value; }.
The initial solution is just to write two wrapper classes, one with T& operator*() and the other one without. Or use inheritance, with iterator deriving from const_iterator (which might be a good solution and has an advantage - we don't need to rewrite comparison operators for iterator and can compare iterator with const_iterator - which most often makes sense - as we check that they both point at same node).
However, I am curious how to write this without inheritance or typing same code twice. Basically, I think that some conditional template generation is needed - to have the method T& operator*() { return node->value; } generated only for iterator and not const_iterator. What is the proper way to do it? If const_iterator treated the Node* as Node*, it almost solves my problem.
Is there a better, "proper" way to do it?
There must be since your solution both has a weird behavior and is also invalid as specified by the C++ standard.
There's a rule called strict aliasing which dictate what kind of pointer type can alias another type. For example, both char* and std::byte* can alias any type, so this code is valid:
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
}
But, you cannot make any type alias another:
double a;
int* b = reinterpret_cast<int*>(&a); // NOT ALLOWED, undefined behavior
In the C++ type system, each instantiation of a template type are different, unrelated types. So in your example, Node<int> is a completely, unrelated, different type than Node<int const>.
I also said that your code has a very strange behavior?
Consider this code:
struct A {
int n;
A(int _n) : n(_n) { std::cout << "construct " << n << std::endl; }
A(A const&) { std::cout << "copy " << n << std::endl; }
~A() { std::cout << "destruct " << n << std::endl; }
};
Node<A> node1{A{1}};
Node<A> node2{A{2}};
Node<A> node3{A{3}};
node1.next = &node2;
node2.next = &node3;
Node<A const> node_const = node1;
This will output the following:
construct 1
construct 2
construct 3
copy 1
destruct 1
destruct 3
destruct 2
destruct 1
As you can see, you copy only one data, but not the rest of the nodes.
What can you do?
In the comments you mentionned that you wanted to implement a const iterator. That can be done without changing your data structures:
// 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;
};
Since you contain a pointer to constant node, you cannot mutate the value inside of the node. The expression node->value yield a T const&.
Since the nodes are there only to implement List, I will assume they are abstracted away completely and never exposed to the users of the list.
If so, then you never have to convert a node, and operate on pointer to constant inside the implementation of the list and its iterators.
To reuse the same iterator, I would do something like this:
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>>;
Then simply make your iterator type parameterized by the boolean:
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>;
Maybe you want another class altogether, like this:
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) {
}
};
Demo
If however you are talking about an iterator or a fancy pointer (as you mention in the comments), it's quite easy to do with an additional template parameter and some std::conditional:
template<typename T, bool C = false>
class Iterator {
public:
using Pointer = std::conditional_t<C, T const*, T*>;
using Reference = std::conditional_t<C, T const&, T&>;
Iterator(Pointer element) :
element(element) {
}
Iterator(Iterator<T, false> const& other) :
element(other.element) {
}
auto operator*() -> Reference {
return *element;
}
private:
Pointer element;
friend Iterator<T, !C>;
};
Demo
I have a class like so:
class Node {
// data
Node* next;
void operator++(int);
};
And when I define the post-increment operator like so:
void Node::operator++(int) {
this = this->next;
}
I get the error Expression is not assignable on this = this->next;.
What's up with this? How do I get this to point to next?
Don't even try. It won't work, and if you could make it work, it would be a bad thing anyway.
From the looks of things, your node is in something like a linked list. If that's the case, then what you normally want is something like:
template <class T>
class linked_list {
class node {
node *next;
T data;
};
node *head;
public:
class iterator {
node *n;
public:
iterator(node *n=NULL) : n(n) {}
iterator &operator++() {
n=n->next;
return *this;
}
bool operator!=(iterator const &other) const {
return n != other.n;
}
// ...
};
iterator begin() { return iterator(n); }
iterator end() { return iterator(); }
};
This can not be changed. it is constant.
According to the C++ standard the this pointer is a prvalue expression (see [class.this]), however the assignment operator requires an lvalue as its left operand (see [expr.ass]).
You cannot do that. Even if by some dirty tricks you change this, the change won't be propagated to the caller. this is a hidden formal argument of methods.
You just discovered why the STL has iterators. An iterator is holding a pointer to a node as a member of itself or in some cases it is a pointer. It could also modify it's member variable. Maybe try something like:
class Iterator {
Node* node;
public:
Iterator(Node* n)
: node( n )
{}
Iterator& operator++()
{
node = node->next;
return *this;
}
Iterator operator++(int)
{
Node* current = node;
node = node->next;
return Iterator(current);
}
Node& operator*()
{
return *node;
}
const Node& operator*() const; // etc...
};
You seem very confused in terms of what you are trying to do here. You have a node class which represents a node in the list. Typically one would also have an iterator class which represents the position in the list that you are currently at. It seems to me that you are trying to use ++ to iterate to the next item in a list, but to do that you would need the iterator as a class of it's own.
In general it makes no sense to assign to the this pointer - this refers to the current object context, you can't just change that context by setting the this pointer.
I hope this will help you somewhat.
The hidden parameter 'this' is passed with the 'const' qualifier. Hence it cannot be changed.
Hidden this
I have the following class:
class list {
private:
struct node {
node() { data=T(); prev=next=this; }
˜node() {}
T data;
node *prev;
node *next;
};
public:
class iterator {
public:
iterator() : p(NULL) {}
T & operator*() { return p->data; }
iterator & operator++()
{ p = p->next; return *this; }
iterator & operator++(int)
{ iterator tmp = *this; ++*this; return *tmp; }
bool operator==(const iterator & rhs) const
{ return p == rhs.p; }
bool operator!=(const iterator & rhs) const
{ return p != rhs.p; }
private:
friend class list<T>;
iterator(node *p) : p(p) {}
node *p;
};
iterator begin() { return iterator(head->next); }
iterator end() { return iterator(head); }
list();
˜list();
etc ...
private:
int N;
node *head;
node *findnode(int);
};
I see that the begin() function returns a constructor for the iterator class. Does this mean that a new iterator is created when it is called? If so, will the memory be recycled after the variable that this iterator is assigned to goes out of scope? I'm slightly confused, for the iterator constructor has no return type. If someone could shed some light on my issues, I would be most appreciative.
Yes a new iterator is created when begin is called. At a high level, yes the memory the iterator occupies will be recycled after it goes out of scope.
Constructors do not have return types as they are called in place on the memory they are to initialize.
At a somewhat lower level, variables that are declared on the stack have their destructor called when they go out of scope, which is one aspect of "recycling". The memory they occupy is on the stack rather than in the heap (as it would be if new were called), so it doesn't get freed in the sense of doing a delete or garbage collection. Rather the memory the variable had occupied may sit idle for a while or be immediately overwritten depending on if stack frames are being removed or added immediately after it goes out of scope (i.e the functions are returning up the stack or new calls are being made).