Double Linked List Using Smart Pointers











up vote
4
down vote

favorite
1












I have decided to rewrite what I did here, following the suggestions to use smart pointers. I will rewrite the other data structures as well using smart pointers where appropriate.



I just want to see how my code stands now, I am sure there are still areas I need to improve or fix. I again want to thank this community in their effort in evaluating my code, I really appreciate it and I believe it is slowly but surely taking my coding skills to the next level.



Here is my header file:



#ifndef DOUBLELINKEDLIST_h
#define DOUBLELINKEDLIST_h


template <class T>
class DoubleLinkedList {
private:

struct Node {
T data;
std::unique_ptr<Node> next = nullptr;
Node* previous = nullptr;

template<typename... Args, typename = std::enable_if_t<std::is_constructible<T, Args&&...>::value>>
explicit Node(std::unique_ptr<Node>&& next, Node* previous, Args&&... args) noexcept(std::is_nothrow_constructible<T, Args&&...>::value)
: data{ std::forward<Args>(args)... }, previous{previous}, next{ std::move(next) } {}

// disable if noncopyable<T> for cleaner error msgs
explicit Node(const T& x, std::unique_ptr<Node>&& p = nullptr)
: data(x)
, next(std::move(p)) {}

// disable if nonmovable<T> for cleaner error msgs
explicit Node(T&& x, std::unique_ptr<Node>&& p = nullptr)
: data(std::move(x))
, next(std::move(p)) {}
};
std::unique_ptr<Node> head = nullptr;
Node* tail = nullptr;

void do_pop_front() {
head = std::move(head->next);
if (!tail) tail = head.get(); // update tail if list was empty before
}

public:
// Constructors
DoubleLinkedList() = default; // empty constructor
DoubleLinkedList(DoubleLinkedList const &source); // copy constructor

// Rule of 5
DoubleLinkedList(DoubleLinkedList &&move) noexcept; // move constructor
DoubleLinkedList& operator=(DoubleLinkedList &&move) noexcept; // move assignment operator
~DoubleLinkedList() noexcept;

// Overload operators
DoubleLinkedList& operator=(DoubleLinkedList const &rhs);

// Create an iterator class
class iterator;
iterator begin();
iterator end();
iterator before_begin();

// Create const iterator class
class const_iterator;
const_iterator cbegin() const;
const_iterator cend() const;
const_iterator begin() const;
const_iterator end() const;
const_iterator before_begin() const;
const_iterator cbefore_begin() const;

// Reverse iteator
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
reverse_iterator rbegin() noexcept { return { end() }; }
const_reverse_iterator rbegin() const noexcept { return { end() }; }
const_reverse_iterator crbegin() const noexcept { return { end() }; }

reverse_iterator rend() noexcept { return { begin() }; }
const_reverse_iterator rend() const noexcept { return { begin() }; }
const_reverse_iterator crend() const noexcept { return { begin() }; }

// Memeber functions
void swap(DoubleLinkedList &other) noexcept;
bool empty() const { return head.get() == nullptr; }
int size() const;

template<typename... Args>
void emplace_back(Args&&... args);

template<typename... Args>
void emplace_front(Args&&... args);

template<typename... Args>
iterator emplace(const_iterator pos, Args&&... args);

void push_back(const T &theData);
void push_back(T &&theData);
void push_front(const T &theData);
void push_front(T &&theData);
iterator insert(const_iterator pos, const T& theData);
iterator insert(const_iterator pos, T&& theData);
void clear();
void pop_front();
void pop_back();
iterator erase(const_iterator pos);
bool search(const T &x);


};

template <class T>
class DoubleLinkedList<T>::iterator {
Node* node = nullptr;
bool end_reached = true;

public:
friend class DoubleLinkedList<T>;

using iterator_category = std::bidirectional_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = T * ;
using reference = T & ;

iterator(Node* node = nullptr, bool end_reached = false) : node{ node }, end_reached{ end_reached } {}

operator const_iterator() const noexcept { return const_iterator{ node }; }
bool operator!=(iterator other) const noexcept;
bool operator==(iterator other) const noexcept;

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

iterator& operator++();
iterator operator++(int);
iterator& operator--();
iterator operator--(int);
};

template <class T>
class DoubleLinkedList<T>::const_iterator {
Node* node = nullptr;
bool end_reached = true;

public:
friend class DoubleLinkedList<T>;

using iterator_category = std::bidirectional_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = const T *;
using reference = const T &;

const_iterator() = default;
const_iterator(Node* node, bool end_reached = false) : node{ node }, end_reached { end_reached } {}


bool operator!=(const_iterator other) const noexcept;
bool operator==(const_iterator other) const noexcept;

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

const_iterator& operator++();
const_iterator operator++(int);
const_iterator& operator--();
const_iterator operator--(int);
};

template <class T>
DoubleLinkedList<T>::DoubleLinkedList(DoubleLinkedList<T> const &source) {
for (Node* loop = source.head.get(); loop != nullptr; loop = loop->next.get()) {
push_back(loop->data);
}
}

template <class T>
DoubleLinkedList<T>::DoubleLinkedList(DoubleLinkedList<T>&& move) noexcept {
move.swap(*this);
}

template <class T>
DoubleLinkedList<T>& DoubleLinkedList<T>::operator=(DoubleLinkedList<T> &&move) noexcept {
move.swap(*this);
return *this;
}

template <class T>
DoubleLinkedList<T>::~DoubleLinkedList() {
clear();
}

template <class T>
void DoubleLinkedList<T>::clear() {
while (head) {
do_pop_front();
}
}

template <class T>
DoubleLinkedList<T>& DoubleLinkedList<T>::operator=(DoubleLinkedList const &rhs) {
SingleLinkedList copy{ rhs };
swap(copy);
return *this;
}

template <class T>
void DoubleLinkedList<T>::swap(DoubleLinkedList &other) noexcept {
using std::swap;
swap(head, other.head);
swap(tail, other.tail);
}

template <class T>
int DoubleLinkedList<T>::size() const {
int size = 0;
for (auto current = head.get(); current != nullptr; current = current->next.get()) {
size++;
}
return size;
}

template <class T>
template <typename... Args>
void DoubleLinkedList<T>::emplace_back(Args&&... args) {
if (!head) emplace_front(std::forward<Args>(args)...);
else {
tail->next = std::make_unique<Node>(nullptr, tail, std::forward<Args>(args)...);
tail = tail->next.get();
}
}

template <class T>
template <typename... Args>
void DoubleLinkedList<T>::emplace_front(Args&&... args) {
head = std::make_unique<Node>(std::move(head), nullptr, std::forward<Args>(args)...);
if (!tail) tail = head.get(); // update tail if list was empty before
}


template <class T>
template <typename... Args>
typename DoubleLinkedList<T>::iterator DoubleLinkedList<T>::emplace(const_iterator pos, Args&&... args) {
if (pos.end_reached) {
emplace_back(std::forward<Args>(args)...);
return end();
}

if (!head) {
emplace_front(std::forward<Args>(args)...);
return begin();
}
std::unique_ptr<Node> newNode = std::make_unique<Node>(std::forward<Args>(args)...);
newNode->previous = pos.node->previous;
newNode->next = std::move(pos.node->previous->next);
pos.node->previous = newNode.get();
newNode->previous->next = std::move(newNode);

return {pos.node->previous};
}

template <class T>
void DoubleLinkedList<T>::push_back(const T &theData) {
std::unique_ptr<Node> newNode = std::make_unique<Node>(std::move(theData));
newNode->previous = tail;

if (!head) {
head = std::move(newNode);
tail = head.get();
}
else {
tail->next = std::move(newNode);
tail = tail->next.get();
}
}

template <class T>
void DoubleLinkedList<T>::push_back(T &&thedata) {
std::unique_ptr<Node> newNode = std::make_unique<Node>(std::move(thedata));
newNode->previous = tail;

if (!head) {
head = std::move(newNode);
tail = head.get();
}

else {
tail->next = std::move(newNode);
tail = tail->next.get();
}
}


template <class T>
void DoubleLinkedList<T>::push_front(const T &theData) {
head = std::make_unique<Node>(std::move(head), nullptr, theData);

if (!(head->next)) {
tail = head.get();
}
}

template <class T>
void DoubleLinkedList<T>::push_front(T &&theData) {
head = std::make_unique<Node>(std::move(head),nullptr,std::move(theData));

if (!(head->next)) {
tail = head.get();
}
}


template <class T>
typename DoubleLinkedList<T>::iterator DoubleLinkedList<T>::insert(const_iterator pos, const T& theData) {
return emplace(pos, theData);
}

template <class T>
typename DoubleLinkedList<T>::iterator DoubleLinkedList<T>::insert(const_iterator pos, T&& theData) {
return emplace(pos, std::move(theData));
}

template <class T>
void DoubleLinkedList<T>::pop_front() {
if (empty()) {
throw std::out_of_range("List is Empty!!! Deletion is not possible.");
}

do_pop_front();
}

template <class T>
void DoubleLinkedList<T>::pop_back() {
if (!head) {
return;
}

if (head) {
auto current = head.get();
Node* prev = nullptr;
while (current->next) {
prev = current;
current = current->next.get();
}
tail = prev;
prev->next = nullptr;
}
else {
throw std::out_of_range("The list is empty, nothing to delete.");
}
}

template <class T>
typename DoubleLinkedList<T>::iterator DoubleLinkedList<T>::erase(const_iterator pos) {
if (pos.end_reached) {
pop_back();
return end();
}

if (pos.node && pos.node->next) {
pos.node->next = std::move(pos.node->previous->next);
return { pos.node->previous };
}

return begin();
}

template <class T>
bool DoubleLinkedList<T>::search(const T &x) {
return std::find(begin(), end(), x) != end();
}

template <typename T>
std::ostream& operator<<(std::ostream &str, DoubleLinkedList<T>& list) {
for (auto const& item : list) {
str << item << "t";
}
return str;
}


// Iterator Implementaion////////////////////////////////////////////////
template <class T>
typename DoubleLinkedList<T>::iterator& DoubleLinkedList<T>::iterator::operator++() {
if (!node) return *this;

if (node->next) {
node = node->next.get();
}

else {
end_reached = true; // keep last node, so we can go backwards if required
}

return *this;
}

template<typename T>
typename DoubleLinkedList<T>::iterator DoubleLinkedList<T>::iterator::operator++(int) {
auto copy = *this;
++*this;
return copy;
}

template <class T>
typename DoubleLinkedList<T>::iterator& DoubleLinkedList<T>::iterator::operator--() {
if (!node) return *this;

if (end_reached) {
end_reached = false;
}

else if (node->previous) {
node = node->previous.get();
}

return *this;
}

template<typename T>
typename DoubleLinkedList<T>::iterator DoubleLinkedList<T>::iterator::operator--(int) {
auto copy = *this;
--*this;
return copy;
}

template<typename T>
bool DoubleLinkedList<T>::iterator::operator==(iterator other) const noexcept {
if (end_reached) return other.end_reached;

if (other.end_reached) return false;

return node == other.node;
}

template<typename T>
bool DoubleLinkedList<T>::iterator::operator!=(iterator other) const noexcept {
return !(*this == other);
}

template<class T>
typename DoubleLinkedList<T>::iterator DoubleLinkedList<T>::begin() {
return head.get();
}

template<class T>
typename DoubleLinkedList<T>::iterator DoubleLinkedList<T>::end() {
return {tail, true};
}

template <class T>
typename DoubleLinkedList<T>::iterator DoubleLinkedList<T>::before_begin() {
return { head.get(), false };
}

// Const Iterator Implementaion////////////////////////////////////////////////
template <class T>
typename DoubleLinkedList<T>::const_iterator& DoubleLinkedList<T>::const_iterator::operator++() {
if (!node) return *this;

if (node->next) {
node = node->next.get();
}

else {
end_reached = true; // keep last node, so we can go backwards if required
}

return *this;
}

template<typename T>
typename DoubleLinkedList<T>::const_iterator DoubleLinkedList<T>::const_iterator::operator++(int) {
auto copy = *this;
++*this;
return copy;
}

template <class T>
typename DoubleLinkedList<T>::const_iterator& DoubleLinkedList<T>::const_iterator::operator--() {
if (!node) return *this;

if (end_reached) {
end_reached = false;
}

else if (node->previous) {
node = node->previous.get();
}

return *this;
}

template<class T>
typename DoubleLinkedList<T>::const_iterator DoubleLinkedList<T>::const_iterator::operator--(int) {
auto copy = *this;
--*this;
return copy;
}

template<class T>
bool DoubleLinkedList<T>::const_iterator::operator==(const_iterator other) const noexcept {
if (end_reached) return other.end_reached;

if (other.end_reached) return false;

return node == other.node;
}

template<class T >
bool DoubleLinkedList<T>::const_iterator::operator!=(const_iterator other) const noexcept {
return !(*this == other);
}


template <class T>
typename DoubleLinkedList<T>::const_iterator DoubleLinkedList<T>::begin() const {
return head.get();
}

template <class T>
typename DoubleLinkedList<T>::const_iterator DoubleLinkedList<T>::end() const {
return {tail, true};
}

template <class T>
typename DoubleLinkedList<T>::const_iterator DoubleLinkedList<T>::cbegin() const {
return begin();
}

template <class T>
typename DoubleLinkedList<T>::const_iterator DoubleLinkedList<T>::cend() const {
return end();
}

template <class T>
typename DoubleLinkedList<T>::const_iterator DoubleLinkedList<T>::before_begin() const {
return { head.get(), true };
}

template <class T>
typename DoubleLinkedList<T>::const_iterator DoubleLinkedList<T>::cbefore_begin() const {
return before_begin();
}


#endif


Here is the main.cpp file:



#include <iostream>
#include <iterator>
#include <memory>
#include <utility>
#include <stdexcept>
#include <iosfwd>
#include <type_traits>
#include <ostream>
#include "SingleLinkedList.h"
#include "DoubleLinkedList.h"

int main(int argc, const char * argv) {

///////////////////////////////////////////////////////////////////////
///////////////////////////// Double Linked List //////////////////////
///////////////////////////////////////////////////////////////////////
DoubleLinkedList<int> obj;
obj.push_back(2);
obj.push_back(4);
obj.push_back(6);
obj.push_back(8);
obj.push_back(10);
std::cout<<"n--------------------------------------------------n";
std::cout<<"---------------displaying all nodes---------------";
std::cout<<"n--------------------------------------------------n";
std::cout << obj << "n";

std::cout<<"n--------------------------------------------------n";
std::cout<<"----------------Inserting At Start----------------";
std::cout<<"n--------------------------------------------------n";
obj.push_front(1);
std::cout << obj << "n";

std::cout << "n--------------------------------------------------n";
std::cout << "-------------Get current size ---=--------------------";
std::cout << "n--------------------------------------------------n";
std::cout << obj.size() << "n";

std::cout<<"n--------------------------------------------------n";
std::cout<<"----------------deleting at start-----------------";
std::cout<<"n--------------------------------------------------n";
obj.pop_front();
std::cout << obj << "n";

std::cout<<"n--------------------------------------------------n";
std::cout<<"----------------deleting at end-----------------------";
std::cout<<"n--------------------------------------------------n";
obj.pop_back();
std::cout << obj << "n";

std::cout<<"n--------------------------------------------------n";
std::cout<<"-------------inserting at particular--------------";
std::cout<<"n--------------------------------------------------n";
obj.insert(obj.cend(),60);
std::cout << obj << "n";

std::cout<<"n----------------------------------------------------------n";
std::cout<<"--------------Deleting after particular position--------------";
std::cout<<"n-----------------------------------------------------------n";
obj.erase(obj.cend());
std::cout << obj << "n";

obj.search(8) ? printf("yes"):printf("no");

std::cout << "n--------------------------------------------------n";
std::cout << "--------------Testing copy----------------------------";
std::cout << "n--------------------------------------------------n";
DoubleLinkedList<int> obj1 = obj;
std::cout << obj1 << "n";

std::cin.get();
}









share|improve this question


























    up vote
    4
    down vote

    favorite
    1












    I have decided to rewrite what I did here, following the suggestions to use smart pointers. I will rewrite the other data structures as well using smart pointers where appropriate.



    I just want to see how my code stands now, I am sure there are still areas I need to improve or fix. I again want to thank this community in their effort in evaluating my code, I really appreciate it and I believe it is slowly but surely taking my coding skills to the next level.



    Here is my header file:



    #ifndef DOUBLELINKEDLIST_h
    #define DOUBLELINKEDLIST_h


    template <class T>
    class DoubleLinkedList {
    private:

    struct Node {
    T data;
    std::unique_ptr<Node> next = nullptr;
    Node* previous = nullptr;

    template<typename... Args, typename = std::enable_if_t<std::is_constructible<T, Args&&...>::value>>
    explicit Node(std::unique_ptr<Node>&& next, Node* previous, Args&&... args) noexcept(std::is_nothrow_constructible<T, Args&&...>::value)
    : data{ std::forward<Args>(args)... }, previous{previous}, next{ std::move(next) } {}

    // disable if noncopyable<T> for cleaner error msgs
    explicit Node(const T& x, std::unique_ptr<Node>&& p = nullptr)
    : data(x)
    , next(std::move(p)) {}

    // disable if nonmovable<T> for cleaner error msgs
    explicit Node(T&& x, std::unique_ptr<Node>&& p = nullptr)
    : data(std::move(x))
    , next(std::move(p)) {}
    };
    std::unique_ptr<Node> head = nullptr;
    Node* tail = nullptr;

    void do_pop_front() {
    head = std::move(head->next);
    if (!tail) tail = head.get(); // update tail if list was empty before
    }

    public:
    // Constructors
    DoubleLinkedList() = default; // empty constructor
    DoubleLinkedList(DoubleLinkedList const &source); // copy constructor

    // Rule of 5
    DoubleLinkedList(DoubleLinkedList &&move) noexcept; // move constructor
    DoubleLinkedList& operator=(DoubleLinkedList &&move) noexcept; // move assignment operator
    ~DoubleLinkedList() noexcept;

    // Overload operators
    DoubleLinkedList& operator=(DoubleLinkedList const &rhs);

    // Create an iterator class
    class iterator;
    iterator begin();
    iterator end();
    iterator before_begin();

    // Create const iterator class
    class const_iterator;
    const_iterator cbegin() const;
    const_iterator cend() const;
    const_iterator begin() const;
    const_iterator end() const;
    const_iterator before_begin() const;
    const_iterator cbefore_begin() const;

    // Reverse iteator
    using reverse_iterator = std::reverse_iterator<iterator>;
    using const_reverse_iterator = std::reverse_iterator<const_iterator>;
    reverse_iterator rbegin() noexcept { return { end() }; }
    const_reverse_iterator rbegin() const noexcept { return { end() }; }
    const_reverse_iterator crbegin() const noexcept { return { end() }; }

    reverse_iterator rend() noexcept { return { begin() }; }
    const_reverse_iterator rend() const noexcept { return { begin() }; }
    const_reverse_iterator crend() const noexcept { return { begin() }; }

    // Memeber functions
    void swap(DoubleLinkedList &other) noexcept;
    bool empty() const { return head.get() == nullptr; }
    int size() const;

    template<typename... Args>
    void emplace_back(Args&&... args);

    template<typename... Args>
    void emplace_front(Args&&... args);

    template<typename... Args>
    iterator emplace(const_iterator pos, Args&&... args);

    void push_back(const T &theData);
    void push_back(T &&theData);
    void push_front(const T &theData);
    void push_front(T &&theData);
    iterator insert(const_iterator pos, const T& theData);
    iterator insert(const_iterator pos, T&& theData);
    void clear();
    void pop_front();
    void pop_back();
    iterator erase(const_iterator pos);
    bool search(const T &x);


    };

    template <class T>
    class DoubleLinkedList<T>::iterator {
    Node* node = nullptr;
    bool end_reached = true;

    public:
    friend class DoubleLinkedList<T>;

    using iterator_category = std::bidirectional_iterator_tag;
    using value_type = T;
    using difference_type = std::ptrdiff_t;
    using pointer = T * ;
    using reference = T & ;

    iterator(Node* node = nullptr, bool end_reached = false) : node{ node }, end_reached{ end_reached } {}

    operator const_iterator() const noexcept { return const_iterator{ node }; }
    bool operator!=(iterator other) const noexcept;
    bool operator==(iterator other) const noexcept;

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

    iterator& operator++();
    iterator operator++(int);
    iterator& operator--();
    iterator operator--(int);
    };

    template <class T>
    class DoubleLinkedList<T>::const_iterator {
    Node* node = nullptr;
    bool end_reached = true;

    public:
    friend class DoubleLinkedList<T>;

    using iterator_category = std::bidirectional_iterator_tag;
    using value_type = T;
    using difference_type = std::ptrdiff_t;
    using pointer = const T *;
    using reference = const T &;

    const_iterator() = default;
    const_iterator(Node* node, bool end_reached = false) : node{ node }, end_reached { end_reached } {}


    bool operator!=(const_iterator other) const noexcept;
    bool operator==(const_iterator other) const noexcept;

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

    const_iterator& operator++();
    const_iterator operator++(int);
    const_iterator& operator--();
    const_iterator operator--(int);
    };

    template <class T>
    DoubleLinkedList<T>::DoubleLinkedList(DoubleLinkedList<T> const &source) {
    for (Node* loop = source.head.get(); loop != nullptr; loop = loop->next.get()) {
    push_back(loop->data);
    }
    }

    template <class T>
    DoubleLinkedList<T>::DoubleLinkedList(DoubleLinkedList<T>&& move) noexcept {
    move.swap(*this);
    }

    template <class T>
    DoubleLinkedList<T>& DoubleLinkedList<T>::operator=(DoubleLinkedList<T> &&move) noexcept {
    move.swap(*this);
    return *this;
    }

    template <class T>
    DoubleLinkedList<T>::~DoubleLinkedList() {
    clear();
    }

    template <class T>
    void DoubleLinkedList<T>::clear() {
    while (head) {
    do_pop_front();
    }
    }

    template <class T>
    DoubleLinkedList<T>& DoubleLinkedList<T>::operator=(DoubleLinkedList const &rhs) {
    SingleLinkedList copy{ rhs };
    swap(copy);
    return *this;
    }

    template <class T>
    void DoubleLinkedList<T>::swap(DoubleLinkedList &other) noexcept {
    using std::swap;
    swap(head, other.head);
    swap(tail, other.tail);
    }

    template <class T>
    int DoubleLinkedList<T>::size() const {
    int size = 0;
    for (auto current = head.get(); current != nullptr; current = current->next.get()) {
    size++;
    }
    return size;
    }

    template <class T>
    template <typename... Args>
    void DoubleLinkedList<T>::emplace_back(Args&&... args) {
    if (!head) emplace_front(std::forward<Args>(args)...);
    else {
    tail->next = std::make_unique<Node>(nullptr, tail, std::forward<Args>(args)...);
    tail = tail->next.get();
    }
    }

    template <class T>
    template <typename... Args>
    void DoubleLinkedList<T>::emplace_front(Args&&... args) {
    head = std::make_unique<Node>(std::move(head), nullptr, std::forward<Args>(args)...);
    if (!tail) tail = head.get(); // update tail if list was empty before
    }


    template <class T>
    template <typename... Args>
    typename DoubleLinkedList<T>::iterator DoubleLinkedList<T>::emplace(const_iterator pos, Args&&... args) {
    if (pos.end_reached) {
    emplace_back(std::forward<Args>(args)...);
    return end();
    }

    if (!head) {
    emplace_front(std::forward<Args>(args)...);
    return begin();
    }
    std::unique_ptr<Node> newNode = std::make_unique<Node>(std::forward<Args>(args)...);
    newNode->previous = pos.node->previous;
    newNode->next = std::move(pos.node->previous->next);
    pos.node->previous = newNode.get();
    newNode->previous->next = std::move(newNode);

    return {pos.node->previous};
    }

    template <class T>
    void DoubleLinkedList<T>::push_back(const T &theData) {
    std::unique_ptr<Node> newNode = std::make_unique<Node>(std::move(theData));
    newNode->previous = tail;

    if (!head) {
    head = std::move(newNode);
    tail = head.get();
    }
    else {
    tail->next = std::move(newNode);
    tail = tail->next.get();
    }
    }

    template <class T>
    void DoubleLinkedList<T>::push_back(T &&thedata) {
    std::unique_ptr<Node> newNode = std::make_unique<Node>(std::move(thedata));
    newNode->previous = tail;

    if (!head) {
    head = std::move(newNode);
    tail = head.get();
    }

    else {
    tail->next = std::move(newNode);
    tail = tail->next.get();
    }
    }


    template <class T>
    void DoubleLinkedList<T>::push_front(const T &theData) {
    head = std::make_unique<Node>(std::move(head), nullptr, theData);

    if (!(head->next)) {
    tail = head.get();
    }
    }

    template <class T>
    void DoubleLinkedList<T>::push_front(T &&theData) {
    head = std::make_unique<Node>(std::move(head),nullptr,std::move(theData));

    if (!(head->next)) {
    tail = head.get();
    }
    }


    template <class T>
    typename DoubleLinkedList<T>::iterator DoubleLinkedList<T>::insert(const_iterator pos, const T& theData) {
    return emplace(pos, theData);
    }

    template <class T>
    typename DoubleLinkedList<T>::iterator DoubleLinkedList<T>::insert(const_iterator pos, T&& theData) {
    return emplace(pos, std::move(theData));
    }

    template <class T>
    void DoubleLinkedList<T>::pop_front() {
    if (empty()) {
    throw std::out_of_range("List is Empty!!! Deletion is not possible.");
    }

    do_pop_front();
    }

    template <class T>
    void DoubleLinkedList<T>::pop_back() {
    if (!head) {
    return;
    }

    if (head) {
    auto current = head.get();
    Node* prev = nullptr;
    while (current->next) {
    prev = current;
    current = current->next.get();
    }
    tail = prev;
    prev->next = nullptr;
    }
    else {
    throw std::out_of_range("The list is empty, nothing to delete.");
    }
    }

    template <class T>
    typename DoubleLinkedList<T>::iterator DoubleLinkedList<T>::erase(const_iterator pos) {
    if (pos.end_reached) {
    pop_back();
    return end();
    }

    if (pos.node && pos.node->next) {
    pos.node->next = std::move(pos.node->previous->next);
    return { pos.node->previous };
    }

    return begin();
    }

    template <class T>
    bool DoubleLinkedList<T>::search(const T &x) {
    return std::find(begin(), end(), x) != end();
    }

    template <typename T>
    std::ostream& operator<<(std::ostream &str, DoubleLinkedList<T>& list) {
    for (auto const& item : list) {
    str << item << "t";
    }
    return str;
    }


    // Iterator Implementaion////////////////////////////////////////////////
    template <class T>
    typename DoubleLinkedList<T>::iterator& DoubleLinkedList<T>::iterator::operator++() {
    if (!node) return *this;

    if (node->next) {
    node = node->next.get();
    }

    else {
    end_reached = true; // keep last node, so we can go backwards if required
    }

    return *this;
    }

    template<typename T>
    typename DoubleLinkedList<T>::iterator DoubleLinkedList<T>::iterator::operator++(int) {
    auto copy = *this;
    ++*this;
    return copy;
    }

    template <class T>
    typename DoubleLinkedList<T>::iterator& DoubleLinkedList<T>::iterator::operator--() {
    if (!node) return *this;

    if (end_reached) {
    end_reached = false;
    }

    else if (node->previous) {
    node = node->previous.get();
    }

    return *this;
    }

    template<typename T>
    typename DoubleLinkedList<T>::iterator DoubleLinkedList<T>::iterator::operator--(int) {
    auto copy = *this;
    --*this;
    return copy;
    }

    template<typename T>
    bool DoubleLinkedList<T>::iterator::operator==(iterator other) const noexcept {
    if (end_reached) return other.end_reached;

    if (other.end_reached) return false;

    return node == other.node;
    }

    template<typename T>
    bool DoubleLinkedList<T>::iterator::operator!=(iterator other) const noexcept {
    return !(*this == other);
    }

    template<class T>
    typename DoubleLinkedList<T>::iterator DoubleLinkedList<T>::begin() {
    return head.get();
    }

    template<class T>
    typename DoubleLinkedList<T>::iterator DoubleLinkedList<T>::end() {
    return {tail, true};
    }

    template <class T>
    typename DoubleLinkedList<T>::iterator DoubleLinkedList<T>::before_begin() {
    return { head.get(), false };
    }

    // Const Iterator Implementaion////////////////////////////////////////////////
    template <class T>
    typename DoubleLinkedList<T>::const_iterator& DoubleLinkedList<T>::const_iterator::operator++() {
    if (!node) return *this;

    if (node->next) {
    node = node->next.get();
    }

    else {
    end_reached = true; // keep last node, so we can go backwards if required
    }

    return *this;
    }

    template<typename T>
    typename DoubleLinkedList<T>::const_iterator DoubleLinkedList<T>::const_iterator::operator++(int) {
    auto copy = *this;
    ++*this;
    return copy;
    }

    template <class T>
    typename DoubleLinkedList<T>::const_iterator& DoubleLinkedList<T>::const_iterator::operator--() {
    if (!node) return *this;

    if (end_reached) {
    end_reached = false;
    }

    else if (node->previous) {
    node = node->previous.get();
    }

    return *this;
    }

    template<class T>
    typename DoubleLinkedList<T>::const_iterator DoubleLinkedList<T>::const_iterator::operator--(int) {
    auto copy = *this;
    --*this;
    return copy;
    }

    template<class T>
    bool DoubleLinkedList<T>::const_iterator::operator==(const_iterator other) const noexcept {
    if (end_reached) return other.end_reached;

    if (other.end_reached) return false;

    return node == other.node;
    }

    template<class T >
    bool DoubleLinkedList<T>::const_iterator::operator!=(const_iterator other) const noexcept {
    return !(*this == other);
    }


    template <class T>
    typename DoubleLinkedList<T>::const_iterator DoubleLinkedList<T>::begin() const {
    return head.get();
    }

    template <class T>
    typename DoubleLinkedList<T>::const_iterator DoubleLinkedList<T>::end() const {
    return {tail, true};
    }

    template <class T>
    typename DoubleLinkedList<T>::const_iterator DoubleLinkedList<T>::cbegin() const {
    return begin();
    }

    template <class T>
    typename DoubleLinkedList<T>::const_iterator DoubleLinkedList<T>::cend() const {
    return end();
    }

    template <class T>
    typename DoubleLinkedList<T>::const_iterator DoubleLinkedList<T>::before_begin() const {
    return { head.get(), true };
    }

    template <class T>
    typename DoubleLinkedList<T>::const_iterator DoubleLinkedList<T>::cbefore_begin() const {
    return before_begin();
    }


    #endif


    Here is the main.cpp file:



    #include <iostream>
    #include <iterator>
    #include <memory>
    #include <utility>
    #include <stdexcept>
    #include <iosfwd>
    #include <type_traits>
    #include <ostream>
    #include "SingleLinkedList.h"
    #include "DoubleLinkedList.h"

    int main(int argc, const char * argv) {

    ///////////////////////////////////////////////////////////////////////
    ///////////////////////////// Double Linked List //////////////////////
    ///////////////////////////////////////////////////////////////////////
    DoubleLinkedList<int> obj;
    obj.push_back(2);
    obj.push_back(4);
    obj.push_back(6);
    obj.push_back(8);
    obj.push_back(10);
    std::cout<<"n--------------------------------------------------n";
    std::cout<<"---------------displaying all nodes---------------";
    std::cout<<"n--------------------------------------------------n";
    std::cout << obj << "n";

    std::cout<<"n--------------------------------------------------n";
    std::cout<<"----------------Inserting At Start----------------";
    std::cout<<"n--------------------------------------------------n";
    obj.push_front(1);
    std::cout << obj << "n";

    std::cout << "n--------------------------------------------------n";
    std::cout << "-------------Get current size ---=--------------------";
    std::cout << "n--------------------------------------------------n";
    std::cout << obj.size() << "n";

    std::cout<<"n--------------------------------------------------n";
    std::cout<<"----------------deleting at start-----------------";
    std::cout<<"n--------------------------------------------------n";
    obj.pop_front();
    std::cout << obj << "n";

    std::cout<<"n--------------------------------------------------n";
    std::cout<<"----------------deleting at end-----------------------";
    std::cout<<"n--------------------------------------------------n";
    obj.pop_back();
    std::cout << obj << "n";

    std::cout<<"n--------------------------------------------------n";
    std::cout<<"-------------inserting at particular--------------";
    std::cout<<"n--------------------------------------------------n";
    obj.insert(obj.cend(),60);
    std::cout << obj << "n";

    std::cout<<"n----------------------------------------------------------n";
    std::cout<<"--------------Deleting after particular position--------------";
    std::cout<<"n-----------------------------------------------------------n";
    obj.erase(obj.cend());
    std::cout << obj << "n";

    obj.search(8) ? printf("yes"):printf("no");

    std::cout << "n--------------------------------------------------n";
    std::cout << "--------------Testing copy----------------------------";
    std::cout << "n--------------------------------------------------n";
    DoubleLinkedList<int> obj1 = obj;
    std::cout << obj1 << "n";

    std::cin.get();
    }









    share|improve this question
























      up vote
      4
      down vote

      favorite
      1









      up vote
      4
      down vote

      favorite
      1






      1





      I have decided to rewrite what I did here, following the suggestions to use smart pointers. I will rewrite the other data structures as well using smart pointers where appropriate.



      I just want to see how my code stands now, I am sure there are still areas I need to improve or fix. I again want to thank this community in their effort in evaluating my code, I really appreciate it and I believe it is slowly but surely taking my coding skills to the next level.



      Here is my header file:



      #ifndef DOUBLELINKEDLIST_h
      #define DOUBLELINKEDLIST_h


      template <class T>
      class DoubleLinkedList {
      private:

      struct Node {
      T data;
      std::unique_ptr<Node> next = nullptr;
      Node* previous = nullptr;

      template<typename... Args, typename = std::enable_if_t<std::is_constructible<T, Args&&...>::value>>
      explicit Node(std::unique_ptr<Node>&& next, Node* previous, Args&&... args) noexcept(std::is_nothrow_constructible<T, Args&&...>::value)
      : data{ std::forward<Args>(args)... }, previous{previous}, next{ std::move(next) } {}

      // disable if noncopyable<T> for cleaner error msgs
      explicit Node(const T& x, std::unique_ptr<Node>&& p = nullptr)
      : data(x)
      , next(std::move(p)) {}

      // disable if nonmovable<T> for cleaner error msgs
      explicit Node(T&& x, std::unique_ptr<Node>&& p = nullptr)
      : data(std::move(x))
      , next(std::move(p)) {}
      };
      std::unique_ptr<Node> head = nullptr;
      Node* tail = nullptr;

      void do_pop_front() {
      head = std::move(head->next);
      if (!tail) tail = head.get(); // update tail if list was empty before
      }

      public:
      // Constructors
      DoubleLinkedList() = default; // empty constructor
      DoubleLinkedList(DoubleLinkedList const &source); // copy constructor

      // Rule of 5
      DoubleLinkedList(DoubleLinkedList &&move) noexcept; // move constructor
      DoubleLinkedList& operator=(DoubleLinkedList &&move) noexcept; // move assignment operator
      ~DoubleLinkedList() noexcept;

      // Overload operators
      DoubleLinkedList& operator=(DoubleLinkedList const &rhs);

      // Create an iterator class
      class iterator;
      iterator begin();
      iterator end();
      iterator before_begin();

      // Create const iterator class
      class const_iterator;
      const_iterator cbegin() const;
      const_iterator cend() const;
      const_iterator begin() const;
      const_iterator end() const;
      const_iterator before_begin() const;
      const_iterator cbefore_begin() const;

      // Reverse iteator
      using reverse_iterator = std::reverse_iterator<iterator>;
      using const_reverse_iterator = std::reverse_iterator<const_iterator>;
      reverse_iterator rbegin() noexcept { return { end() }; }
      const_reverse_iterator rbegin() const noexcept { return { end() }; }
      const_reverse_iterator crbegin() const noexcept { return { end() }; }

      reverse_iterator rend() noexcept { return { begin() }; }
      const_reverse_iterator rend() const noexcept { return { begin() }; }
      const_reverse_iterator crend() const noexcept { return { begin() }; }

      // Memeber functions
      void swap(DoubleLinkedList &other) noexcept;
      bool empty() const { return head.get() == nullptr; }
      int size() const;

      template<typename... Args>
      void emplace_back(Args&&... args);

      template<typename... Args>
      void emplace_front(Args&&... args);

      template<typename... Args>
      iterator emplace(const_iterator pos, Args&&... args);

      void push_back(const T &theData);
      void push_back(T &&theData);
      void push_front(const T &theData);
      void push_front(T &&theData);
      iterator insert(const_iterator pos, const T& theData);
      iterator insert(const_iterator pos, T&& theData);
      void clear();
      void pop_front();
      void pop_back();
      iterator erase(const_iterator pos);
      bool search(const T &x);


      };

      template <class T>
      class DoubleLinkedList<T>::iterator {
      Node* node = nullptr;
      bool end_reached = true;

      public:
      friend class DoubleLinkedList<T>;

      using iterator_category = std::bidirectional_iterator_tag;
      using value_type = T;
      using difference_type = std::ptrdiff_t;
      using pointer = T * ;
      using reference = T & ;

      iterator(Node* node = nullptr, bool end_reached = false) : node{ node }, end_reached{ end_reached } {}

      operator const_iterator() const noexcept { return const_iterator{ node }; }
      bool operator!=(iterator other) const noexcept;
      bool operator==(iterator other) const noexcept;

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

      iterator& operator++();
      iterator operator++(int);
      iterator& operator--();
      iterator operator--(int);
      };

      template <class T>
      class DoubleLinkedList<T>::const_iterator {
      Node* node = nullptr;
      bool end_reached = true;

      public:
      friend class DoubleLinkedList<T>;

      using iterator_category = std::bidirectional_iterator_tag;
      using value_type = T;
      using difference_type = std::ptrdiff_t;
      using pointer = const T *;
      using reference = const T &;

      const_iterator() = default;
      const_iterator(Node* node, bool end_reached = false) : node{ node }, end_reached { end_reached } {}


      bool operator!=(const_iterator other) const noexcept;
      bool operator==(const_iterator other) const noexcept;

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

      const_iterator& operator++();
      const_iterator operator++(int);
      const_iterator& operator--();
      const_iterator operator--(int);
      };

      template <class T>
      DoubleLinkedList<T>::DoubleLinkedList(DoubleLinkedList<T> const &source) {
      for (Node* loop = source.head.get(); loop != nullptr; loop = loop->next.get()) {
      push_back(loop->data);
      }
      }

      template <class T>
      DoubleLinkedList<T>::DoubleLinkedList(DoubleLinkedList<T>&& move) noexcept {
      move.swap(*this);
      }

      template <class T>
      DoubleLinkedList<T>& DoubleLinkedList<T>::operator=(DoubleLinkedList<T> &&move) noexcept {
      move.swap(*this);
      return *this;
      }

      template <class T>
      DoubleLinkedList<T>::~DoubleLinkedList() {
      clear();
      }

      template <class T>
      void DoubleLinkedList<T>::clear() {
      while (head) {
      do_pop_front();
      }
      }

      template <class T>
      DoubleLinkedList<T>& DoubleLinkedList<T>::operator=(DoubleLinkedList const &rhs) {
      SingleLinkedList copy{ rhs };
      swap(copy);
      return *this;
      }

      template <class T>
      void DoubleLinkedList<T>::swap(DoubleLinkedList &other) noexcept {
      using std::swap;
      swap(head, other.head);
      swap(tail, other.tail);
      }

      template <class T>
      int DoubleLinkedList<T>::size() const {
      int size = 0;
      for (auto current = head.get(); current != nullptr; current = current->next.get()) {
      size++;
      }
      return size;
      }

      template <class T>
      template <typename... Args>
      void DoubleLinkedList<T>::emplace_back(Args&&... args) {
      if (!head) emplace_front(std::forward<Args>(args)...);
      else {
      tail->next = std::make_unique<Node>(nullptr, tail, std::forward<Args>(args)...);
      tail = tail->next.get();
      }
      }

      template <class T>
      template <typename... Args>
      void DoubleLinkedList<T>::emplace_front(Args&&... args) {
      head = std::make_unique<Node>(std::move(head), nullptr, std::forward<Args>(args)...);
      if (!tail) tail = head.get(); // update tail if list was empty before
      }


      template <class T>
      template <typename... Args>
      typename DoubleLinkedList<T>::iterator DoubleLinkedList<T>::emplace(const_iterator pos, Args&&... args) {
      if (pos.end_reached) {
      emplace_back(std::forward<Args>(args)...);
      return end();
      }

      if (!head) {
      emplace_front(std::forward<Args>(args)...);
      return begin();
      }
      std::unique_ptr<Node> newNode = std::make_unique<Node>(std::forward<Args>(args)...);
      newNode->previous = pos.node->previous;
      newNode->next = std::move(pos.node->previous->next);
      pos.node->previous = newNode.get();
      newNode->previous->next = std::move(newNode);

      return {pos.node->previous};
      }

      template <class T>
      void DoubleLinkedList<T>::push_back(const T &theData) {
      std::unique_ptr<Node> newNode = std::make_unique<Node>(std::move(theData));
      newNode->previous = tail;

      if (!head) {
      head = std::move(newNode);
      tail = head.get();
      }
      else {
      tail->next = std::move(newNode);
      tail = tail->next.get();
      }
      }

      template <class T>
      void DoubleLinkedList<T>::push_back(T &&thedata) {
      std::unique_ptr<Node> newNode = std::make_unique<Node>(std::move(thedata));
      newNode->previous = tail;

      if (!head) {
      head = std::move(newNode);
      tail = head.get();
      }

      else {
      tail->next = std::move(newNode);
      tail = tail->next.get();
      }
      }


      template <class T>
      void DoubleLinkedList<T>::push_front(const T &theData) {
      head = std::make_unique<Node>(std::move(head), nullptr, theData);

      if (!(head->next)) {
      tail = head.get();
      }
      }

      template <class T>
      void DoubleLinkedList<T>::push_front(T &&theData) {
      head = std::make_unique<Node>(std::move(head),nullptr,std::move(theData));

      if (!(head->next)) {
      tail = head.get();
      }
      }


      template <class T>
      typename DoubleLinkedList<T>::iterator DoubleLinkedList<T>::insert(const_iterator pos, const T& theData) {
      return emplace(pos, theData);
      }

      template <class T>
      typename DoubleLinkedList<T>::iterator DoubleLinkedList<T>::insert(const_iterator pos, T&& theData) {
      return emplace(pos, std::move(theData));
      }

      template <class T>
      void DoubleLinkedList<T>::pop_front() {
      if (empty()) {
      throw std::out_of_range("List is Empty!!! Deletion is not possible.");
      }

      do_pop_front();
      }

      template <class T>
      void DoubleLinkedList<T>::pop_back() {
      if (!head) {
      return;
      }

      if (head) {
      auto current = head.get();
      Node* prev = nullptr;
      while (current->next) {
      prev = current;
      current = current->next.get();
      }
      tail = prev;
      prev->next = nullptr;
      }
      else {
      throw std::out_of_range("The list is empty, nothing to delete.");
      }
      }

      template <class T>
      typename DoubleLinkedList<T>::iterator DoubleLinkedList<T>::erase(const_iterator pos) {
      if (pos.end_reached) {
      pop_back();
      return end();
      }

      if (pos.node && pos.node->next) {
      pos.node->next = std::move(pos.node->previous->next);
      return { pos.node->previous };
      }

      return begin();
      }

      template <class T>
      bool DoubleLinkedList<T>::search(const T &x) {
      return std::find(begin(), end(), x) != end();
      }

      template <typename T>
      std::ostream& operator<<(std::ostream &str, DoubleLinkedList<T>& list) {
      for (auto const& item : list) {
      str << item << "t";
      }
      return str;
      }


      // Iterator Implementaion////////////////////////////////////////////////
      template <class T>
      typename DoubleLinkedList<T>::iterator& DoubleLinkedList<T>::iterator::operator++() {
      if (!node) return *this;

      if (node->next) {
      node = node->next.get();
      }

      else {
      end_reached = true; // keep last node, so we can go backwards if required
      }

      return *this;
      }

      template<typename T>
      typename DoubleLinkedList<T>::iterator DoubleLinkedList<T>::iterator::operator++(int) {
      auto copy = *this;
      ++*this;
      return copy;
      }

      template <class T>
      typename DoubleLinkedList<T>::iterator& DoubleLinkedList<T>::iterator::operator--() {
      if (!node) return *this;

      if (end_reached) {
      end_reached = false;
      }

      else if (node->previous) {
      node = node->previous.get();
      }

      return *this;
      }

      template<typename T>
      typename DoubleLinkedList<T>::iterator DoubleLinkedList<T>::iterator::operator--(int) {
      auto copy = *this;
      --*this;
      return copy;
      }

      template<typename T>
      bool DoubleLinkedList<T>::iterator::operator==(iterator other) const noexcept {
      if (end_reached) return other.end_reached;

      if (other.end_reached) return false;

      return node == other.node;
      }

      template<typename T>
      bool DoubleLinkedList<T>::iterator::operator!=(iterator other) const noexcept {
      return !(*this == other);
      }

      template<class T>
      typename DoubleLinkedList<T>::iterator DoubleLinkedList<T>::begin() {
      return head.get();
      }

      template<class T>
      typename DoubleLinkedList<T>::iterator DoubleLinkedList<T>::end() {
      return {tail, true};
      }

      template <class T>
      typename DoubleLinkedList<T>::iterator DoubleLinkedList<T>::before_begin() {
      return { head.get(), false };
      }

      // Const Iterator Implementaion////////////////////////////////////////////////
      template <class T>
      typename DoubleLinkedList<T>::const_iterator& DoubleLinkedList<T>::const_iterator::operator++() {
      if (!node) return *this;

      if (node->next) {
      node = node->next.get();
      }

      else {
      end_reached = true; // keep last node, so we can go backwards if required
      }

      return *this;
      }

      template<typename T>
      typename DoubleLinkedList<T>::const_iterator DoubleLinkedList<T>::const_iterator::operator++(int) {
      auto copy = *this;
      ++*this;
      return copy;
      }

      template <class T>
      typename DoubleLinkedList<T>::const_iterator& DoubleLinkedList<T>::const_iterator::operator--() {
      if (!node) return *this;

      if (end_reached) {
      end_reached = false;
      }

      else if (node->previous) {
      node = node->previous.get();
      }

      return *this;
      }

      template<class T>
      typename DoubleLinkedList<T>::const_iterator DoubleLinkedList<T>::const_iterator::operator--(int) {
      auto copy = *this;
      --*this;
      return copy;
      }

      template<class T>
      bool DoubleLinkedList<T>::const_iterator::operator==(const_iterator other) const noexcept {
      if (end_reached) return other.end_reached;

      if (other.end_reached) return false;

      return node == other.node;
      }

      template<class T >
      bool DoubleLinkedList<T>::const_iterator::operator!=(const_iterator other) const noexcept {
      return !(*this == other);
      }


      template <class T>
      typename DoubleLinkedList<T>::const_iterator DoubleLinkedList<T>::begin() const {
      return head.get();
      }

      template <class T>
      typename DoubleLinkedList<T>::const_iterator DoubleLinkedList<T>::end() const {
      return {tail, true};
      }

      template <class T>
      typename DoubleLinkedList<T>::const_iterator DoubleLinkedList<T>::cbegin() const {
      return begin();
      }

      template <class T>
      typename DoubleLinkedList<T>::const_iterator DoubleLinkedList<T>::cend() const {
      return end();
      }

      template <class T>
      typename DoubleLinkedList<T>::const_iterator DoubleLinkedList<T>::before_begin() const {
      return { head.get(), true };
      }

      template <class T>
      typename DoubleLinkedList<T>::const_iterator DoubleLinkedList<T>::cbefore_begin() const {
      return before_begin();
      }


      #endif


      Here is the main.cpp file:



      #include <iostream>
      #include <iterator>
      #include <memory>
      #include <utility>
      #include <stdexcept>
      #include <iosfwd>
      #include <type_traits>
      #include <ostream>
      #include "SingleLinkedList.h"
      #include "DoubleLinkedList.h"

      int main(int argc, const char * argv) {

      ///////////////////////////////////////////////////////////////////////
      ///////////////////////////// Double Linked List //////////////////////
      ///////////////////////////////////////////////////////////////////////
      DoubleLinkedList<int> obj;
      obj.push_back(2);
      obj.push_back(4);
      obj.push_back(6);
      obj.push_back(8);
      obj.push_back(10);
      std::cout<<"n--------------------------------------------------n";
      std::cout<<"---------------displaying all nodes---------------";
      std::cout<<"n--------------------------------------------------n";
      std::cout << obj << "n";

      std::cout<<"n--------------------------------------------------n";
      std::cout<<"----------------Inserting At Start----------------";
      std::cout<<"n--------------------------------------------------n";
      obj.push_front(1);
      std::cout << obj << "n";

      std::cout << "n--------------------------------------------------n";
      std::cout << "-------------Get current size ---=--------------------";
      std::cout << "n--------------------------------------------------n";
      std::cout << obj.size() << "n";

      std::cout<<"n--------------------------------------------------n";
      std::cout<<"----------------deleting at start-----------------";
      std::cout<<"n--------------------------------------------------n";
      obj.pop_front();
      std::cout << obj << "n";

      std::cout<<"n--------------------------------------------------n";
      std::cout<<"----------------deleting at end-----------------------";
      std::cout<<"n--------------------------------------------------n";
      obj.pop_back();
      std::cout << obj << "n";

      std::cout<<"n--------------------------------------------------n";
      std::cout<<"-------------inserting at particular--------------";
      std::cout<<"n--------------------------------------------------n";
      obj.insert(obj.cend(),60);
      std::cout << obj << "n";

      std::cout<<"n----------------------------------------------------------n";
      std::cout<<"--------------Deleting after particular position--------------";
      std::cout<<"n-----------------------------------------------------------n";
      obj.erase(obj.cend());
      std::cout << obj << "n";

      obj.search(8) ? printf("yes"):printf("no");

      std::cout << "n--------------------------------------------------n";
      std::cout << "--------------Testing copy----------------------------";
      std::cout << "n--------------------------------------------------n";
      DoubleLinkedList<int> obj1 = obj;
      std::cout << obj1 << "n";

      std::cin.get();
      }









      share|improve this question













      I have decided to rewrite what I did here, following the suggestions to use smart pointers. I will rewrite the other data structures as well using smart pointers where appropriate.



      I just want to see how my code stands now, I am sure there are still areas I need to improve or fix. I again want to thank this community in their effort in evaluating my code, I really appreciate it and I believe it is slowly but surely taking my coding skills to the next level.



      Here is my header file:



      #ifndef DOUBLELINKEDLIST_h
      #define DOUBLELINKEDLIST_h


      template <class T>
      class DoubleLinkedList {
      private:

      struct Node {
      T data;
      std::unique_ptr<Node> next = nullptr;
      Node* previous = nullptr;

      template<typename... Args, typename = std::enable_if_t<std::is_constructible<T, Args&&...>::value>>
      explicit Node(std::unique_ptr<Node>&& next, Node* previous, Args&&... args) noexcept(std::is_nothrow_constructible<T, Args&&...>::value)
      : data{ std::forward<Args>(args)... }, previous{previous}, next{ std::move(next) } {}

      // disable if noncopyable<T> for cleaner error msgs
      explicit Node(const T& x, std::unique_ptr<Node>&& p = nullptr)
      : data(x)
      , next(std::move(p)) {}

      // disable if nonmovable<T> for cleaner error msgs
      explicit Node(T&& x, std::unique_ptr<Node>&& p = nullptr)
      : data(std::move(x))
      , next(std::move(p)) {}
      };
      std::unique_ptr<Node> head = nullptr;
      Node* tail = nullptr;

      void do_pop_front() {
      head = std::move(head->next);
      if (!tail) tail = head.get(); // update tail if list was empty before
      }

      public:
      // Constructors
      DoubleLinkedList() = default; // empty constructor
      DoubleLinkedList(DoubleLinkedList const &source); // copy constructor

      // Rule of 5
      DoubleLinkedList(DoubleLinkedList &&move) noexcept; // move constructor
      DoubleLinkedList& operator=(DoubleLinkedList &&move) noexcept; // move assignment operator
      ~DoubleLinkedList() noexcept;

      // Overload operators
      DoubleLinkedList& operator=(DoubleLinkedList const &rhs);

      // Create an iterator class
      class iterator;
      iterator begin();
      iterator end();
      iterator before_begin();

      // Create const iterator class
      class const_iterator;
      const_iterator cbegin() const;
      const_iterator cend() const;
      const_iterator begin() const;
      const_iterator end() const;
      const_iterator before_begin() const;
      const_iterator cbefore_begin() const;

      // Reverse iteator
      using reverse_iterator = std::reverse_iterator<iterator>;
      using const_reverse_iterator = std::reverse_iterator<const_iterator>;
      reverse_iterator rbegin() noexcept { return { end() }; }
      const_reverse_iterator rbegin() const noexcept { return { end() }; }
      const_reverse_iterator crbegin() const noexcept { return { end() }; }

      reverse_iterator rend() noexcept { return { begin() }; }
      const_reverse_iterator rend() const noexcept { return { begin() }; }
      const_reverse_iterator crend() const noexcept { return { begin() }; }

      // Memeber functions
      void swap(DoubleLinkedList &other) noexcept;
      bool empty() const { return head.get() == nullptr; }
      int size() const;

      template<typename... Args>
      void emplace_back(Args&&... args);

      template<typename... Args>
      void emplace_front(Args&&... args);

      template<typename... Args>
      iterator emplace(const_iterator pos, Args&&... args);

      void push_back(const T &theData);
      void push_back(T &&theData);
      void push_front(const T &theData);
      void push_front(T &&theData);
      iterator insert(const_iterator pos, const T& theData);
      iterator insert(const_iterator pos, T&& theData);
      void clear();
      void pop_front();
      void pop_back();
      iterator erase(const_iterator pos);
      bool search(const T &x);


      };

      template <class T>
      class DoubleLinkedList<T>::iterator {
      Node* node = nullptr;
      bool end_reached = true;

      public:
      friend class DoubleLinkedList<T>;

      using iterator_category = std::bidirectional_iterator_tag;
      using value_type = T;
      using difference_type = std::ptrdiff_t;
      using pointer = T * ;
      using reference = T & ;

      iterator(Node* node = nullptr, bool end_reached = false) : node{ node }, end_reached{ end_reached } {}

      operator const_iterator() const noexcept { return const_iterator{ node }; }
      bool operator!=(iterator other) const noexcept;
      bool operator==(iterator other) const noexcept;

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

      iterator& operator++();
      iterator operator++(int);
      iterator& operator--();
      iterator operator--(int);
      };

      template <class T>
      class DoubleLinkedList<T>::const_iterator {
      Node* node = nullptr;
      bool end_reached = true;

      public:
      friend class DoubleLinkedList<T>;

      using iterator_category = std::bidirectional_iterator_tag;
      using value_type = T;
      using difference_type = std::ptrdiff_t;
      using pointer = const T *;
      using reference = const T &;

      const_iterator() = default;
      const_iterator(Node* node, bool end_reached = false) : node{ node }, end_reached { end_reached } {}


      bool operator!=(const_iterator other) const noexcept;
      bool operator==(const_iterator other) const noexcept;

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

      const_iterator& operator++();
      const_iterator operator++(int);
      const_iterator& operator--();
      const_iterator operator--(int);
      };

      template <class T>
      DoubleLinkedList<T>::DoubleLinkedList(DoubleLinkedList<T> const &source) {
      for (Node* loop = source.head.get(); loop != nullptr; loop = loop->next.get()) {
      push_back(loop->data);
      }
      }

      template <class T>
      DoubleLinkedList<T>::DoubleLinkedList(DoubleLinkedList<T>&& move) noexcept {
      move.swap(*this);
      }

      template <class T>
      DoubleLinkedList<T>& DoubleLinkedList<T>::operator=(DoubleLinkedList<T> &&move) noexcept {
      move.swap(*this);
      return *this;
      }

      template <class T>
      DoubleLinkedList<T>::~DoubleLinkedList() {
      clear();
      }

      template <class T>
      void DoubleLinkedList<T>::clear() {
      while (head) {
      do_pop_front();
      }
      }

      template <class T>
      DoubleLinkedList<T>& DoubleLinkedList<T>::operator=(DoubleLinkedList const &rhs) {
      SingleLinkedList copy{ rhs };
      swap(copy);
      return *this;
      }

      template <class T>
      void DoubleLinkedList<T>::swap(DoubleLinkedList &other) noexcept {
      using std::swap;
      swap(head, other.head);
      swap(tail, other.tail);
      }

      template <class T>
      int DoubleLinkedList<T>::size() const {
      int size = 0;
      for (auto current = head.get(); current != nullptr; current = current->next.get()) {
      size++;
      }
      return size;
      }

      template <class T>
      template <typename... Args>
      void DoubleLinkedList<T>::emplace_back(Args&&... args) {
      if (!head) emplace_front(std::forward<Args>(args)...);
      else {
      tail->next = std::make_unique<Node>(nullptr, tail, std::forward<Args>(args)...);
      tail = tail->next.get();
      }
      }

      template <class T>
      template <typename... Args>
      void DoubleLinkedList<T>::emplace_front(Args&&... args) {
      head = std::make_unique<Node>(std::move(head), nullptr, std::forward<Args>(args)...);
      if (!tail) tail = head.get(); // update tail if list was empty before
      }


      template <class T>
      template <typename... Args>
      typename DoubleLinkedList<T>::iterator DoubleLinkedList<T>::emplace(const_iterator pos, Args&&... args) {
      if (pos.end_reached) {
      emplace_back(std::forward<Args>(args)...);
      return end();
      }

      if (!head) {
      emplace_front(std::forward<Args>(args)...);
      return begin();
      }
      std::unique_ptr<Node> newNode = std::make_unique<Node>(std::forward<Args>(args)...);
      newNode->previous = pos.node->previous;
      newNode->next = std::move(pos.node->previous->next);
      pos.node->previous = newNode.get();
      newNode->previous->next = std::move(newNode);

      return {pos.node->previous};
      }

      template <class T>
      void DoubleLinkedList<T>::push_back(const T &theData) {
      std::unique_ptr<Node> newNode = std::make_unique<Node>(std::move(theData));
      newNode->previous = tail;

      if (!head) {
      head = std::move(newNode);
      tail = head.get();
      }
      else {
      tail->next = std::move(newNode);
      tail = tail->next.get();
      }
      }

      template <class T>
      void DoubleLinkedList<T>::push_back(T &&thedata) {
      std::unique_ptr<Node> newNode = std::make_unique<Node>(std::move(thedata));
      newNode->previous = tail;

      if (!head) {
      head = std::move(newNode);
      tail = head.get();
      }

      else {
      tail->next = std::move(newNode);
      tail = tail->next.get();
      }
      }


      template <class T>
      void DoubleLinkedList<T>::push_front(const T &theData) {
      head = std::make_unique<Node>(std::move(head), nullptr, theData);

      if (!(head->next)) {
      tail = head.get();
      }
      }

      template <class T>
      void DoubleLinkedList<T>::push_front(T &&theData) {
      head = std::make_unique<Node>(std::move(head),nullptr,std::move(theData));

      if (!(head->next)) {
      tail = head.get();
      }
      }


      template <class T>
      typename DoubleLinkedList<T>::iterator DoubleLinkedList<T>::insert(const_iterator pos, const T& theData) {
      return emplace(pos, theData);
      }

      template <class T>
      typename DoubleLinkedList<T>::iterator DoubleLinkedList<T>::insert(const_iterator pos, T&& theData) {
      return emplace(pos, std::move(theData));
      }

      template <class T>
      void DoubleLinkedList<T>::pop_front() {
      if (empty()) {
      throw std::out_of_range("List is Empty!!! Deletion is not possible.");
      }

      do_pop_front();
      }

      template <class T>
      void DoubleLinkedList<T>::pop_back() {
      if (!head) {
      return;
      }

      if (head) {
      auto current = head.get();
      Node* prev = nullptr;
      while (current->next) {
      prev = current;
      current = current->next.get();
      }
      tail = prev;
      prev->next = nullptr;
      }
      else {
      throw std::out_of_range("The list is empty, nothing to delete.");
      }
      }

      template <class T>
      typename DoubleLinkedList<T>::iterator DoubleLinkedList<T>::erase(const_iterator pos) {
      if (pos.end_reached) {
      pop_back();
      return end();
      }

      if (pos.node && pos.node->next) {
      pos.node->next = std::move(pos.node->previous->next);
      return { pos.node->previous };
      }

      return begin();
      }

      template <class T>
      bool DoubleLinkedList<T>::search(const T &x) {
      return std::find(begin(), end(), x) != end();
      }

      template <typename T>
      std::ostream& operator<<(std::ostream &str, DoubleLinkedList<T>& list) {
      for (auto const& item : list) {
      str << item << "t";
      }
      return str;
      }


      // Iterator Implementaion////////////////////////////////////////////////
      template <class T>
      typename DoubleLinkedList<T>::iterator& DoubleLinkedList<T>::iterator::operator++() {
      if (!node) return *this;

      if (node->next) {
      node = node->next.get();
      }

      else {
      end_reached = true; // keep last node, so we can go backwards if required
      }

      return *this;
      }

      template<typename T>
      typename DoubleLinkedList<T>::iterator DoubleLinkedList<T>::iterator::operator++(int) {
      auto copy = *this;
      ++*this;
      return copy;
      }

      template <class T>
      typename DoubleLinkedList<T>::iterator& DoubleLinkedList<T>::iterator::operator--() {
      if (!node) return *this;

      if (end_reached) {
      end_reached = false;
      }

      else if (node->previous) {
      node = node->previous.get();
      }

      return *this;
      }

      template<typename T>
      typename DoubleLinkedList<T>::iterator DoubleLinkedList<T>::iterator::operator--(int) {
      auto copy = *this;
      --*this;
      return copy;
      }

      template<typename T>
      bool DoubleLinkedList<T>::iterator::operator==(iterator other) const noexcept {
      if (end_reached) return other.end_reached;

      if (other.end_reached) return false;

      return node == other.node;
      }

      template<typename T>
      bool DoubleLinkedList<T>::iterator::operator!=(iterator other) const noexcept {
      return !(*this == other);
      }

      template<class T>
      typename DoubleLinkedList<T>::iterator DoubleLinkedList<T>::begin() {
      return head.get();
      }

      template<class T>
      typename DoubleLinkedList<T>::iterator DoubleLinkedList<T>::end() {
      return {tail, true};
      }

      template <class T>
      typename DoubleLinkedList<T>::iterator DoubleLinkedList<T>::before_begin() {
      return { head.get(), false };
      }

      // Const Iterator Implementaion////////////////////////////////////////////////
      template <class T>
      typename DoubleLinkedList<T>::const_iterator& DoubleLinkedList<T>::const_iterator::operator++() {
      if (!node) return *this;

      if (node->next) {
      node = node->next.get();
      }

      else {
      end_reached = true; // keep last node, so we can go backwards if required
      }

      return *this;
      }

      template<typename T>
      typename DoubleLinkedList<T>::const_iterator DoubleLinkedList<T>::const_iterator::operator++(int) {
      auto copy = *this;
      ++*this;
      return copy;
      }

      template <class T>
      typename DoubleLinkedList<T>::const_iterator& DoubleLinkedList<T>::const_iterator::operator--() {
      if (!node) return *this;

      if (end_reached) {
      end_reached = false;
      }

      else if (node->previous) {
      node = node->previous.get();
      }

      return *this;
      }

      template<class T>
      typename DoubleLinkedList<T>::const_iterator DoubleLinkedList<T>::const_iterator::operator--(int) {
      auto copy = *this;
      --*this;
      return copy;
      }

      template<class T>
      bool DoubleLinkedList<T>::const_iterator::operator==(const_iterator other) const noexcept {
      if (end_reached) return other.end_reached;

      if (other.end_reached) return false;

      return node == other.node;
      }

      template<class T >
      bool DoubleLinkedList<T>::const_iterator::operator!=(const_iterator other) const noexcept {
      return !(*this == other);
      }


      template <class T>
      typename DoubleLinkedList<T>::const_iterator DoubleLinkedList<T>::begin() const {
      return head.get();
      }

      template <class T>
      typename DoubleLinkedList<T>::const_iterator DoubleLinkedList<T>::end() const {
      return {tail, true};
      }

      template <class T>
      typename DoubleLinkedList<T>::const_iterator DoubleLinkedList<T>::cbegin() const {
      return begin();
      }

      template <class T>
      typename DoubleLinkedList<T>::const_iterator DoubleLinkedList<T>::cend() const {
      return end();
      }

      template <class T>
      typename DoubleLinkedList<T>::const_iterator DoubleLinkedList<T>::before_begin() const {
      return { head.get(), true };
      }

      template <class T>
      typename DoubleLinkedList<T>::const_iterator DoubleLinkedList<T>::cbefore_begin() const {
      return before_begin();
      }


      #endif


      Here is the main.cpp file:



      #include <iostream>
      #include <iterator>
      #include <memory>
      #include <utility>
      #include <stdexcept>
      #include <iosfwd>
      #include <type_traits>
      #include <ostream>
      #include "SingleLinkedList.h"
      #include "DoubleLinkedList.h"

      int main(int argc, const char * argv) {

      ///////////////////////////////////////////////////////////////////////
      ///////////////////////////// Double Linked List //////////////////////
      ///////////////////////////////////////////////////////////////////////
      DoubleLinkedList<int> obj;
      obj.push_back(2);
      obj.push_back(4);
      obj.push_back(6);
      obj.push_back(8);
      obj.push_back(10);
      std::cout<<"n--------------------------------------------------n";
      std::cout<<"---------------displaying all nodes---------------";
      std::cout<<"n--------------------------------------------------n";
      std::cout << obj << "n";

      std::cout<<"n--------------------------------------------------n";
      std::cout<<"----------------Inserting At Start----------------";
      std::cout<<"n--------------------------------------------------n";
      obj.push_front(1);
      std::cout << obj << "n";

      std::cout << "n--------------------------------------------------n";
      std::cout << "-------------Get current size ---=--------------------";
      std::cout << "n--------------------------------------------------n";
      std::cout << obj.size() << "n";

      std::cout<<"n--------------------------------------------------n";
      std::cout<<"----------------deleting at start-----------------";
      std::cout<<"n--------------------------------------------------n";
      obj.pop_front();
      std::cout << obj << "n";

      std::cout<<"n--------------------------------------------------n";
      std::cout<<"----------------deleting at end-----------------------";
      std::cout<<"n--------------------------------------------------n";
      obj.pop_back();
      std::cout << obj << "n";

      std::cout<<"n--------------------------------------------------n";
      std::cout<<"-------------inserting at particular--------------";
      std::cout<<"n--------------------------------------------------n";
      obj.insert(obj.cend(),60);
      std::cout << obj << "n";

      std::cout<<"n----------------------------------------------------------n";
      std::cout<<"--------------Deleting after particular position--------------";
      std::cout<<"n-----------------------------------------------------------n";
      obj.erase(obj.cend());
      std::cout << obj << "n";

      obj.search(8) ? printf("yes"):printf("no");

      std::cout << "n--------------------------------------------------n";
      std::cout << "--------------Testing copy----------------------------";
      std::cout << "n--------------------------------------------------n";
      DoubleLinkedList<int> obj1 = obj;
      std::cout << obj1 << "n";

      std::cin.get();
      }






      c++ linked-list pointers






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Aug 23 at 19:24









      Snorrlaxxx

      43618




      43618






















          2 Answers
          2






          active

          oldest

          votes

















          up vote
          5
          down vote



          accepted










          It is great to see you are really taking reviews seriously and are trying to learn something and improve yourself. That really makes us reviewers like what we are doing. I may not be the best here, but I will still try :)



          I very much like your approach with smart pointers (unique_ptr). I do not think that was that trivial as JDługosz stated it. I was also wondering about all the explicit constructors in Node but then spotted the emplace and then it clicked (before going down the rabbit hole of reading the previous reviews).



          The main problem with the code is, that it is big and hard to review. I had to copy-paste it to editor to review it. I would personally organise it a bit differently and will tell you why:



          Method declaration vs. body



          It may appear to be good to first declare the class with all the methods and stuff and then later define bodies for all the methods, maybe because you got used to header + source pair. I have a bit different opinion about this. Splitting it like this, especially when the body is small, not only makes you type a lot more than you need, but it makes it harder to see the logic as well, harder to review, harder to check, harder to maintain. I can understand that the declarative part could serve as a documentation (see what it provides separated from how it does it), but there are other tools for documentation and seeing such things... (so I prefer inline body, most of the time, if it is not too big.)



          Documentation



          Documenting your code properly is very important and there are good tools to help you, namely doxygen. Try it. I believe you will understand how valuable /// documenting comments can be. ///< inline documentation as well But leave your hints (like // copy constructor) in normal comments or remove these completely (such things should become obvious). But do comment the logic if it is not trivial (maybe with links like this).



          The rule of five or three or ... copy and swap



          I can understand that you are still learning, but maybe it is time to actually understand what it does, how it does it and what are the alternatives. Just follow the link for full explanation and consider this:



          template <class T>
          class DoubleLinkedList {
          public:
          // see https://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom
          DoubleLinkedList& operator=(DoubleLinkedList other) {
          swap(*this, other);
          return *this;
          }
          //...
          }


          do_pop_front()



          if (!tail) tail = head.get(); // update tail if list was empty before


          Really? This does not sound right. Did you test it?
          ...and we are back again to documentation: there are many (at least three) versions of doubly-linked lists:




          1. Null-terminated on both ends (what your version appears to be)

          2. Cyclic (head->previous == tail, tail->next == head, unless empty or you make the very list an empty node, which is easiest to implement)

          3. Hybrid (last->next = nullptr, first->prev == last ... this has some advantages, you need only one pointer in the list and you can still easily terminate the for-each loop on nullptr ... but not the reverse_iterator).


          Which implementation is it trully? Maybe something else? You should document it:



          /// Doubly Linked List with both ends null-terminated
          template <class T>
          class DoubleLinkedList {


          or maybe use ///brief and some other features doxygen knows (a bit like JavaDoc).






          share|improve this answer



















          • 1




            I think you meant head->previous == tail and tail->next == head for the cyclic version. // Re inline member function definitions: Yes, they are nice and make the code shorter. However, they make it harder to get a general overview of the members and member functions. I'd let that one fall under personal preference, as there is no clear "winner" in terms of readability. (Of course, if you're using some external documentation tool like doxygen, then you can use that to get an overview, but not everybody uses those.)
            – hoffmale
            Aug 23 at 20:50






          • 1




            @hoffmale: spot on with head->previous, thank you, will fix that. As for inline bodies, sure, that is quite opinion-based and I gave my reasons which led to doxygen which should become good habbit even if you do not actually export the documentation. Everybody should learn to document the code properly.
            – firda
            Aug 23 at 20:52










          • @hoffmale Hey have you been on Skype? Sent you a message a few days ago.
            – Snorrlaxxx
            Aug 28 at 16:39










          • @hoffmale Hey do you have internet yet? Messaged you a few times on Skype to see if you are available.
            – Snorrlaxxx
            Sep 26 at 19:18


















          up vote
          3
          down vote













          #include <iostream>
          #include <iterator>
          #include <memory>
          #include <utility>
          #include <stdexcept>
          #include <iosfwd>
          #include <type_traits>
          #include <ostream>
          #include "SingleLinkedList.h"
          #include "DoubleLinkedList.h"


          Why is all of that in your main.cpp file? You have no includes in your header file. I looked back at your previous implementations and you don't seem to have includes in any of those header files either. I'm guessing here but I believe you are relying on inclusion dependency for your implementation to work. Move your user defined header to the top of the include list and it will break functionality. Header files should be self-contained such that if I want to use your class it won't matter what order I declare it and I won't have to declare it's dependencies.



          Your solution should be twofold.




          1. Move all the required includes into your header file.

          2. Order your includes from local to global scale.


          What I mean by 2 is this:





          1. h file corresponding to this cpp file (if applicable)

          2. headers from the same component,

          3. headers from other components,

          4. system headers.




          Taken verbatim from this answer.



          It is also often recommended to sort headers alphabetically within each category.



          *You also don't need "SingleLinkedList.h" in your Double linked list example usage file.






          share|improve this answer





















          • Thanks for the answer. Just so I understand you suggest putting all my include files in the header file and then just leave the include .h files in the main.cpp?
            – Snorrlaxxx
            Aug 24 at 17:45






          • 2




            @Snorrlaxxx: Not all headers, just the ones required for the DoubleLinkedList to work, i.e. <iterator>, <memory>, <utility>, <stdexcept>, <type_traits> and <ostream>. // <iostream> is only needed inside main(), and <iosfwd> is redundant if <iostream> is already included.
            – hoffmale
            Aug 24 at 18:30












          • @hoffmale I see and those headers need to be included in the .h file?
            – Snorrlaxxx
            Aug 24 at 19:11






          • 2




            @Snorrlaxxx: Yes, to make the header self-contained, that including it does not require knowing what tools are used inside and what headers add to make it working. Don't worry, headers are included only once even if #include-d multiple times. That's what the header-guards are for: #ifndef DOUBLELINKEDLIST_h
            – firda
            Sep 26 at 19:25











          Your Answer





          StackExchange.ifUsing("editor", function () {
          return StackExchange.using("mathjaxEditing", function () {
          StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
          StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
          });
          });
          }, "mathjax-editing");

          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "196"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          convertImagesToLinks: false,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f202333%2fdouble-linked-list-using-smart-pointers%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          2 Answers
          2






          active

          oldest

          votes








          2 Answers
          2






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes








          up vote
          5
          down vote



          accepted










          It is great to see you are really taking reviews seriously and are trying to learn something and improve yourself. That really makes us reviewers like what we are doing. I may not be the best here, but I will still try :)



          I very much like your approach with smart pointers (unique_ptr). I do not think that was that trivial as JDługosz stated it. I was also wondering about all the explicit constructors in Node but then spotted the emplace and then it clicked (before going down the rabbit hole of reading the previous reviews).



          The main problem with the code is, that it is big and hard to review. I had to copy-paste it to editor to review it. I would personally organise it a bit differently and will tell you why:



          Method declaration vs. body



          It may appear to be good to first declare the class with all the methods and stuff and then later define bodies for all the methods, maybe because you got used to header + source pair. I have a bit different opinion about this. Splitting it like this, especially when the body is small, not only makes you type a lot more than you need, but it makes it harder to see the logic as well, harder to review, harder to check, harder to maintain. I can understand that the declarative part could serve as a documentation (see what it provides separated from how it does it), but there are other tools for documentation and seeing such things... (so I prefer inline body, most of the time, if it is not too big.)



          Documentation



          Documenting your code properly is very important and there are good tools to help you, namely doxygen. Try it. I believe you will understand how valuable /// documenting comments can be. ///< inline documentation as well But leave your hints (like // copy constructor) in normal comments or remove these completely (such things should become obvious). But do comment the logic if it is not trivial (maybe with links like this).



          The rule of five or three or ... copy and swap



          I can understand that you are still learning, but maybe it is time to actually understand what it does, how it does it and what are the alternatives. Just follow the link for full explanation and consider this:



          template <class T>
          class DoubleLinkedList {
          public:
          // see https://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom
          DoubleLinkedList& operator=(DoubleLinkedList other) {
          swap(*this, other);
          return *this;
          }
          //...
          }


          do_pop_front()



          if (!tail) tail = head.get(); // update tail if list was empty before


          Really? This does not sound right. Did you test it?
          ...and we are back again to documentation: there are many (at least three) versions of doubly-linked lists:




          1. Null-terminated on both ends (what your version appears to be)

          2. Cyclic (head->previous == tail, tail->next == head, unless empty or you make the very list an empty node, which is easiest to implement)

          3. Hybrid (last->next = nullptr, first->prev == last ... this has some advantages, you need only one pointer in the list and you can still easily terminate the for-each loop on nullptr ... but not the reverse_iterator).


          Which implementation is it trully? Maybe something else? You should document it:



          /// Doubly Linked List with both ends null-terminated
          template <class T>
          class DoubleLinkedList {


          or maybe use ///brief and some other features doxygen knows (a bit like JavaDoc).






          share|improve this answer



















          • 1




            I think you meant head->previous == tail and tail->next == head for the cyclic version. // Re inline member function definitions: Yes, they are nice and make the code shorter. However, they make it harder to get a general overview of the members and member functions. I'd let that one fall under personal preference, as there is no clear "winner" in terms of readability. (Of course, if you're using some external documentation tool like doxygen, then you can use that to get an overview, but not everybody uses those.)
            – hoffmale
            Aug 23 at 20:50






          • 1




            @hoffmale: spot on with head->previous, thank you, will fix that. As for inline bodies, sure, that is quite opinion-based and I gave my reasons which led to doxygen which should become good habbit even if you do not actually export the documentation. Everybody should learn to document the code properly.
            – firda
            Aug 23 at 20:52










          • @hoffmale Hey have you been on Skype? Sent you a message a few days ago.
            – Snorrlaxxx
            Aug 28 at 16:39










          • @hoffmale Hey do you have internet yet? Messaged you a few times on Skype to see if you are available.
            – Snorrlaxxx
            Sep 26 at 19:18















          up vote
          5
          down vote



          accepted










          It is great to see you are really taking reviews seriously and are trying to learn something and improve yourself. That really makes us reviewers like what we are doing. I may not be the best here, but I will still try :)



          I very much like your approach with smart pointers (unique_ptr). I do not think that was that trivial as JDługosz stated it. I was also wondering about all the explicit constructors in Node but then spotted the emplace and then it clicked (before going down the rabbit hole of reading the previous reviews).



          The main problem with the code is, that it is big and hard to review. I had to copy-paste it to editor to review it. I would personally organise it a bit differently and will tell you why:



          Method declaration vs. body



          It may appear to be good to first declare the class with all the methods and stuff and then later define bodies for all the methods, maybe because you got used to header + source pair. I have a bit different opinion about this. Splitting it like this, especially when the body is small, not only makes you type a lot more than you need, but it makes it harder to see the logic as well, harder to review, harder to check, harder to maintain. I can understand that the declarative part could serve as a documentation (see what it provides separated from how it does it), but there are other tools for documentation and seeing such things... (so I prefer inline body, most of the time, if it is not too big.)



          Documentation



          Documenting your code properly is very important and there are good tools to help you, namely doxygen. Try it. I believe you will understand how valuable /// documenting comments can be. ///< inline documentation as well But leave your hints (like // copy constructor) in normal comments or remove these completely (such things should become obvious). But do comment the logic if it is not trivial (maybe with links like this).



          The rule of five or three or ... copy and swap



          I can understand that you are still learning, but maybe it is time to actually understand what it does, how it does it and what are the alternatives. Just follow the link for full explanation and consider this:



          template <class T>
          class DoubleLinkedList {
          public:
          // see https://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom
          DoubleLinkedList& operator=(DoubleLinkedList other) {
          swap(*this, other);
          return *this;
          }
          //...
          }


          do_pop_front()



          if (!tail) tail = head.get(); // update tail if list was empty before


          Really? This does not sound right. Did you test it?
          ...and we are back again to documentation: there are many (at least three) versions of doubly-linked lists:




          1. Null-terminated on both ends (what your version appears to be)

          2. Cyclic (head->previous == tail, tail->next == head, unless empty or you make the very list an empty node, which is easiest to implement)

          3. Hybrid (last->next = nullptr, first->prev == last ... this has some advantages, you need only one pointer in the list and you can still easily terminate the for-each loop on nullptr ... but not the reverse_iterator).


          Which implementation is it trully? Maybe something else? You should document it:



          /// Doubly Linked List with both ends null-terminated
          template <class T>
          class DoubleLinkedList {


          or maybe use ///brief and some other features doxygen knows (a bit like JavaDoc).






          share|improve this answer



















          • 1




            I think you meant head->previous == tail and tail->next == head for the cyclic version. // Re inline member function definitions: Yes, they are nice and make the code shorter. However, they make it harder to get a general overview of the members and member functions. I'd let that one fall under personal preference, as there is no clear "winner" in terms of readability. (Of course, if you're using some external documentation tool like doxygen, then you can use that to get an overview, but not everybody uses those.)
            – hoffmale
            Aug 23 at 20:50






          • 1




            @hoffmale: spot on with head->previous, thank you, will fix that. As for inline bodies, sure, that is quite opinion-based and I gave my reasons which led to doxygen which should become good habbit even if you do not actually export the documentation. Everybody should learn to document the code properly.
            – firda
            Aug 23 at 20:52










          • @hoffmale Hey have you been on Skype? Sent you a message a few days ago.
            – Snorrlaxxx
            Aug 28 at 16:39










          • @hoffmale Hey do you have internet yet? Messaged you a few times on Skype to see if you are available.
            – Snorrlaxxx
            Sep 26 at 19:18













          up vote
          5
          down vote



          accepted







          up vote
          5
          down vote



          accepted






          It is great to see you are really taking reviews seriously and are trying to learn something and improve yourself. That really makes us reviewers like what we are doing. I may not be the best here, but I will still try :)



          I very much like your approach with smart pointers (unique_ptr). I do not think that was that trivial as JDługosz stated it. I was also wondering about all the explicit constructors in Node but then spotted the emplace and then it clicked (before going down the rabbit hole of reading the previous reviews).



          The main problem with the code is, that it is big and hard to review. I had to copy-paste it to editor to review it. I would personally organise it a bit differently and will tell you why:



          Method declaration vs. body



          It may appear to be good to first declare the class with all the methods and stuff and then later define bodies for all the methods, maybe because you got used to header + source pair. I have a bit different opinion about this. Splitting it like this, especially when the body is small, not only makes you type a lot more than you need, but it makes it harder to see the logic as well, harder to review, harder to check, harder to maintain. I can understand that the declarative part could serve as a documentation (see what it provides separated from how it does it), but there are other tools for documentation and seeing such things... (so I prefer inline body, most of the time, if it is not too big.)



          Documentation



          Documenting your code properly is very important and there are good tools to help you, namely doxygen. Try it. I believe you will understand how valuable /// documenting comments can be. ///< inline documentation as well But leave your hints (like // copy constructor) in normal comments or remove these completely (such things should become obvious). But do comment the logic if it is not trivial (maybe with links like this).



          The rule of five or three or ... copy and swap



          I can understand that you are still learning, but maybe it is time to actually understand what it does, how it does it and what are the alternatives. Just follow the link for full explanation and consider this:



          template <class T>
          class DoubleLinkedList {
          public:
          // see https://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom
          DoubleLinkedList& operator=(DoubleLinkedList other) {
          swap(*this, other);
          return *this;
          }
          //...
          }


          do_pop_front()



          if (!tail) tail = head.get(); // update tail if list was empty before


          Really? This does not sound right. Did you test it?
          ...and we are back again to documentation: there are many (at least three) versions of doubly-linked lists:




          1. Null-terminated on both ends (what your version appears to be)

          2. Cyclic (head->previous == tail, tail->next == head, unless empty or you make the very list an empty node, which is easiest to implement)

          3. Hybrid (last->next = nullptr, first->prev == last ... this has some advantages, you need only one pointer in the list and you can still easily terminate the for-each loop on nullptr ... but not the reverse_iterator).


          Which implementation is it trully? Maybe something else? You should document it:



          /// Doubly Linked List with both ends null-terminated
          template <class T>
          class DoubleLinkedList {


          or maybe use ///brief and some other features doxygen knows (a bit like JavaDoc).






          share|improve this answer














          It is great to see you are really taking reviews seriously and are trying to learn something and improve yourself. That really makes us reviewers like what we are doing. I may not be the best here, but I will still try :)



          I very much like your approach with smart pointers (unique_ptr). I do not think that was that trivial as JDługosz stated it. I was also wondering about all the explicit constructors in Node but then spotted the emplace and then it clicked (before going down the rabbit hole of reading the previous reviews).



          The main problem with the code is, that it is big and hard to review. I had to copy-paste it to editor to review it. I would personally organise it a bit differently and will tell you why:



          Method declaration vs. body



          It may appear to be good to first declare the class with all the methods and stuff and then later define bodies for all the methods, maybe because you got used to header + source pair. I have a bit different opinion about this. Splitting it like this, especially when the body is small, not only makes you type a lot more than you need, but it makes it harder to see the logic as well, harder to review, harder to check, harder to maintain. I can understand that the declarative part could serve as a documentation (see what it provides separated from how it does it), but there are other tools for documentation and seeing such things... (so I prefer inline body, most of the time, if it is not too big.)



          Documentation



          Documenting your code properly is very important and there are good tools to help you, namely doxygen. Try it. I believe you will understand how valuable /// documenting comments can be. ///< inline documentation as well But leave your hints (like // copy constructor) in normal comments or remove these completely (such things should become obvious). But do comment the logic if it is not trivial (maybe with links like this).



          The rule of five or three or ... copy and swap



          I can understand that you are still learning, but maybe it is time to actually understand what it does, how it does it and what are the alternatives. Just follow the link for full explanation and consider this:



          template <class T>
          class DoubleLinkedList {
          public:
          // see https://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom
          DoubleLinkedList& operator=(DoubleLinkedList other) {
          swap(*this, other);
          return *this;
          }
          //...
          }


          do_pop_front()



          if (!tail) tail = head.get(); // update tail if list was empty before


          Really? This does not sound right. Did you test it?
          ...and we are back again to documentation: there are many (at least three) versions of doubly-linked lists:




          1. Null-terminated on both ends (what your version appears to be)

          2. Cyclic (head->previous == tail, tail->next == head, unless empty or you make the very list an empty node, which is easiest to implement)

          3. Hybrid (last->next = nullptr, first->prev == last ... this has some advantages, you need only one pointer in the list and you can still easily terminate the for-each loop on nullptr ... but not the reverse_iterator).


          Which implementation is it trully? Maybe something else? You should document it:



          /// Doubly Linked List with both ends null-terminated
          template <class T>
          class DoubleLinkedList {


          or maybe use ///brief and some other features doxygen knows (a bit like JavaDoc).







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited 1 hour ago









          albert

          1371




          1371










          answered Aug 23 at 20:33









          firda

          2,842627




          2,842627








          • 1




            I think you meant head->previous == tail and tail->next == head for the cyclic version. // Re inline member function definitions: Yes, they are nice and make the code shorter. However, they make it harder to get a general overview of the members and member functions. I'd let that one fall under personal preference, as there is no clear "winner" in terms of readability. (Of course, if you're using some external documentation tool like doxygen, then you can use that to get an overview, but not everybody uses those.)
            – hoffmale
            Aug 23 at 20:50






          • 1




            @hoffmale: spot on with head->previous, thank you, will fix that. As for inline bodies, sure, that is quite opinion-based and I gave my reasons which led to doxygen which should become good habbit even if you do not actually export the documentation. Everybody should learn to document the code properly.
            – firda
            Aug 23 at 20:52










          • @hoffmale Hey have you been on Skype? Sent you a message a few days ago.
            – Snorrlaxxx
            Aug 28 at 16:39










          • @hoffmale Hey do you have internet yet? Messaged you a few times on Skype to see if you are available.
            – Snorrlaxxx
            Sep 26 at 19:18














          • 1




            I think you meant head->previous == tail and tail->next == head for the cyclic version. // Re inline member function definitions: Yes, they are nice and make the code shorter. However, they make it harder to get a general overview of the members and member functions. I'd let that one fall under personal preference, as there is no clear "winner" in terms of readability. (Of course, if you're using some external documentation tool like doxygen, then you can use that to get an overview, but not everybody uses those.)
            – hoffmale
            Aug 23 at 20:50






          • 1




            @hoffmale: spot on with head->previous, thank you, will fix that. As for inline bodies, sure, that is quite opinion-based and I gave my reasons which led to doxygen which should become good habbit even if you do not actually export the documentation. Everybody should learn to document the code properly.
            – firda
            Aug 23 at 20:52










          • @hoffmale Hey have you been on Skype? Sent you a message a few days ago.
            – Snorrlaxxx
            Aug 28 at 16:39










          • @hoffmale Hey do you have internet yet? Messaged you a few times on Skype to see if you are available.
            – Snorrlaxxx
            Sep 26 at 19:18








          1




          1




          I think you meant head->previous == tail and tail->next == head for the cyclic version. // Re inline member function definitions: Yes, they are nice and make the code shorter. However, they make it harder to get a general overview of the members and member functions. I'd let that one fall under personal preference, as there is no clear "winner" in terms of readability. (Of course, if you're using some external documentation tool like doxygen, then you can use that to get an overview, but not everybody uses those.)
          – hoffmale
          Aug 23 at 20:50




          I think you meant head->previous == tail and tail->next == head for the cyclic version. // Re inline member function definitions: Yes, they are nice and make the code shorter. However, they make it harder to get a general overview of the members and member functions. I'd let that one fall under personal preference, as there is no clear "winner" in terms of readability. (Of course, if you're using some external documentation tool like doxygen, then you can use that to get an overview, but not everybody uses those.)
          – hoffmale
          Aug 23 at 20:50




          1




          1




          @hoffmale: spot on with head->previous, thank you, will fix that. As for inline bodies, sure, that is quite opinion-based and I gave my reasons which led to doxygen which should become good habbit even if you do not actually export the documentation. Everybody should learn to document the code properly.
          – firda
          Aug 23 at 20:52




          @hoffmale: spot on with head->previous, thank you, will fix that. As for inline bodies, sure, that is quite opinion-based and I gave my reasons which led to doxygen which should become good habbit even if you do not actually export the documentation. Everybody should learn to document the code properly.
          – firda
          Aug 23 at 20:52












          @hoffmale Hey have you been on Skype? Sent you a message a few days ago.
          – Snorrlaxxx
          Aug 28 at 16:39




          @hoffmale Hey have you been on Skype? Sent you a message a few days ago.
          – Snorrlaxxx
          Aug 28 at 16:39












          @hoffmale Hey do you have internet yet? Messaged you a few times on Skype to see if you are available.
          – Snorrlaxxx
          Sep 26 at 19:18




          @hoffmale Hey do you have internet yet? Messaged you a few times on Skype to see if you are available.
          – Snorrlaxxx
          Sep 26 at 19:18












          up vote
          3
          down vote













          #include <iostream>
          #include <iterator>
          #include <memory>
          #include <utility>
          #include <stdexcept>
          #include <iosfwd>
          #include <type_traits>
          #include <ostream>
          #include "SingleLinkedList.h"
          #include "DoubleLinkedList.h"


          Why is all of that in your main.cpp file? You have no includes in your header file. I looked back at your previous implementations and you don't seem to have includes in any of those header files either. I'm guessing here but I believe you are relying on inclusion dependency for your implementation to work. Move your user defined header to the top of the include list and it will break functionality. Header files should be self-contained such that if I want to use your class it won't matter what order I declare it and I won't have to declare it's dependencies.



          Your solution should be twofold.




          1. Move all the required includes into your header file.

          2. Order your includes from local to global scale.


          What I mean by 2 is this:





          1. h file corresponding to this cpp file (if applicable)

          2. headers from the same component,

          3. headers from other components,

          4. system headers.




          Taken verbatim from this answer.



          It is also often recommended to sort headers alphabetically within each category.



          *You also don't need "SingleLinkedList.h" in your Double linked list example usage file.






          share|improve this answer





















          • Thanks for the answer. Just so I understand you suggest putting all my include files in the header file and then just leave the include .h files in the main.cpp?
            – Snorrlaxxx
            Aug 24 at 17:45






          • 2




            @Snorrlaxxx: Not all headers, just the ones required for the DoubleLinkedList to work, i.e. <iterator>, <memory>, <utility>, <stdexcept>, <type_traits> and <ostream>. // <iostream> is only needed inside main(), and <iosfwd> is redundant if <iostream> is already included.
            – hoffmale
            Aug 24 at 18:30












          • @hoffmale I see and those headers need to be included in the .h file?
            – Snorrlaxxx
            Aug 24 at 19:11






          • 2




            @Snorrlaxxx: Yes, to make the header self-contained, that including it does not require knowing what tools are used inside and what headers add to make it working. Don't worry, headers are included only once even if #include-d multiple times. That's what the header-guards are for: #ifndef DOUBLELINKEDLIST_h
            – firda
            Sep 26 at 19:25















          up vote
          3
          down vote













          #include <iostream>
          #include <iterator>
          #include <memory>
          #include <utility>
          #include <stdexcept>
          #include <iosfwd>
          #include <type_traits>
          #include <ostream>
          #include "SingleLinkedList.h"
          #include "DoubleLinkedList.h"


          Why is all of that in your main.cpp file? You have no includes in your header file. I looked back at your previous implementations and you don't seem to have includes in any of those header files either. I'm guessing here but I believe you are relying on inclusion dependency for your implementation to work. Move your user defined header to the top of the include list and it will break functionality. Header files should be self-contained such that if I want to use your class it won't matter what order I declare it and I won't have to declare it's dependencies.



          Your solution should be twofold.




          1. Move all the required includes into your header file.

          2. Order your includes from local to global scale.


          What I mean by 2 is this:





          1. h file corresponding to this cpp file (if applicable)

          2. headers from the same component,

          3. headers from other components,

          4. system headers.




          Taken verbatim from this answer.



          It is also often recommended to sort headers alphabetically within each category.



          *You also don't need "SingleLinkedList.h" in your Double linked list example usage file.






          share|improve this answer





















          • Thanks for the answer. Just so I understand you suggest putting all my include files in the header file and then just leave the include .h files in the main.cpp?
            – Snorrlaxxx
            Aug 24 at 17:45






          • 2




            @Snorrlaxxx: Not all headers, just the ones required for the DoubleLinkedList to work, i.e. <iterator>, <memory>, <utility>, <stdexcept>, <type_traits> and <ostream>. // <iostream> is only needed inside main(), and <iosfwd> is redundant if <iostream> is already included.
            – hoffmale
            Aug 24 at 18:30












          • @hoffmale I see and those headers need to be included in the .h file?
            – Snorrlaxxx
            Aug 24 at 19:11






          • 2




            @Snorrlaxxx: Yes, to make the header self-contained, that including it does not require knowing what tools are used inside and what headers add to make it working. Don't worry, headers are included only once even if #include-d multiple times. That's what the header-guards are for: #ifndef DOUBLELINKEDLIST_h
            – firda
            Sep 26 at 19:25













          up vote
          3
          down vote










          up vote
          3
          down vote









          #include <iostream>
          #include <iterator>
          #include <memory>
          #include <utility>
          #include <stdexcept>
          #include <iosfwd>
          #include <type_traits>
          #include <ostream>
          #include "SingleLinkedList.h"
          #include "DoubleLinkedList.h"


          Why is all of that in your main.cpp file? You have no includes in your header file. I looked back at your previous implementations and you don't seem to have includes in any of those header files either. I'm guessing here but I believe you are relying on inclusion dependency for your implementation to work. Move your user defined header to the top of the include list and it will break functionality. Header files should be self-contained such that if I want to use your class it won't matter what order I declare it and I won't have to declare it's dependencies.



          Your solution should be twofold.




          1. Move all the required includes into your header file.

          2. Order your includes from local to global scale.


          What I mean by 2 is this:





          1. h file corresponding to this cpp file (if applicable)

          2. headers from the same component,

          3. headers from other components,

          4. system headers.




          Taken verbatim from this answer.



          It is also often recommended to sort headers alphabetically within each category.



          *You also don't need "SingleLinkedList.h" in your Double linked list example usage file.






          share|improve this answer












          #include <iostream>
          #include <iterator>
          #include <memory>
          #include <utility>
          #include <stdexcept>
          #include <iosfwd>
          #include <type_traits>
          #include <ostream>
          #include "SingleLinkedList.h"
          #include "DoubleLinkedList.h"


          Why is all of that in your main.cpp file? You have no includes in your header file. I looked back at your previous implementations and you don't seem to have includes in any of those header files either. I'm guessing here but I believe you are relying on inclusion dependency for your implementation to work. Move your user defined header to the top of the include list and it will break functionality. Header files should be self-contained such that if I want to use your class it won't matter what order I declare it and I won't have to declare it's dependencies.



          Your solution should be twofold.




          1. Move all the required includes into your header file.

          2. Order your includes from local to global scale.


          What I mean by 2 is this:





          1. h file corresponding to this cpp file (if applicable)

          2. headers from the same component,

          3. headers from other components,

          4. system headers.




          Taken verbatim from this answer.



          It is also often recommended to sort headers alphabetically within each category.



          *You also don't need "SingleLinkedList.h" in your Double linked list example usage file.







          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Aug 24 at 1:29









          bruglesco

          1,3242520




          1,3242520












          • Thanks for the answer. Just so I understand you suggest putting all my include files in the header file and then just leave the include .h files in the main.cpp?
            – Snorrlaxxx
            Aug 24 at 17:45






          • 2




            @Snorrlaxxx: Not all headers, just the ones required for the DoubleLinkedList to work, i.e. <iterator>, <memory>, <utility>, <stdexcept>, <type_traits> and <ostream>. // <iostream> is only needed inside main(), and <iosfwd> is redundant if <iostream> is already included.
            – hoffmale
            Aug 24 at 18:30












          • @hoffmale I see and those headers need to be included in the .h file?
            – Snorrlaxxx
            Aug 24 at 19:11






          • 2




            @Snorrlaxxx: Yes, to make the header self-contained, that including it does not require knowing what tools are used inside and what headers add to make it working. Don't worry, headers are included only once even if #include-d multiple times. That's what the header-guards are for: #ifndef DOUBLELINKEDLIST_h
            – firda
            Sep 26 at 19:25


















          • Thanks for the answer. Just so I understand you suggest putting all my include files in the header file and then just leave the include .h files in the main.cpp?
            – Snorrlaxxx
            Aug 24 at 17:45






          • 2




            @Snorrlaxxx: Not all headers, just the ones required for the DoubleLinkedList to work, i.e. <iterator>, <memory>, <utility>, <stdexcept>, <type_traits> and <ostream>. // <iostream> is only needed inside main(), and <iosfwd> is redundant if <iostream> is already included.
            – hoffmale
            Aug 24 at 18:30












          • @hoffmale I see and those headers need to be included in the .h file?
            – Snorrlaxxx
            Aug 24 at 19:11






          • 2




            @Snorrlaxxx: Yes, to make the header self-contained, that including it does not require knowing what tools are used inside and what headers add to make it working. Don't worry, headers are included only once even if #include-d multiple times. That's what the header-guards are for: #ifndef DOUBLELINKEDLIST_h
            – firda
            Sep 26 at 19:25
















          Thanks for the answer. Just so I understand you suggest putting all my include files in the header file and then just leave the include .h files in the main.cpp?
          – Snorrlaxxx
          Aug 24 at 17:45




          Thanks for the answer. Just so I understand you suggest putting all my include files in the header file and then just leave the include .h files in the main.cpp?
          – Snorrlaxxx
          Aug 24 at 17:45




          2




          2




          @Snorrlaxxx: Not all headers, just the ones required for the DoubleLinkedList to work, i.e. <iterator>, <memory>, <utility>, <stdexcept>, <type_traits> and <ostream>. // <iostream> is only needed inside main(), and <iosfwd> is redundant if <iostream> is already included.
          – hoffmale
          Aug 24 at 18:30






          @Snorrlaxxx: Not all headers, just the ones required for the DoubleLinkedList to work, i.e. <iterator>, <memory>, <utility>, <stdexcept>, <type_traits> and <ostream>. // <iostream> is only needed inside main(), and <iosfwd> is redundant if <iostream> is already included.
          – hoffmale
          Aug 24 at 18:30














          @hoffmale I see and those headers need to be included in the .h file?
          – Snorrlaxxx
          Aug 24 at 19:11




          @hoffmale I see and those headers need to be included in the .h file?
          – Snorrlaxxx
          Aug 24 at 19:11




          2




          2




          @Snorrlaxxx: Yes, to make the header self-contained, that including it does not require knowing what tools are used inside and what headers add to make it working. Don't worry, headers are included only once even if #include-d multiple times. That's what the header-guards are for: #ifndef DOUBLELINKEDLIST_h
          – firda
          Sep 26 at 19:25




          @Snorrlaxxx: Yes, to make the header self-contained, that including it does not require knowing what tools are used inside and what headers add to make it working. Don't worry, headers are included only once even if #include-d multiple times. That's what the header-guards are for: #ifndef DOUBLELINKEDLIST_h
          – firda
          Sep 26 at 19:25


















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Code Review Stack Exchange!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          Use MathJax to format equations. MathJax reference.


          To learn more, see our tips on writing great answers.





          Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


          Please pay close attention to the following guidance:


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f202333%2fdouble-linked-list-using-smart-pointers%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          Ellipse (mathématiques)

          Quarter-circle Tiles

          Mont Emei