- > `Node` wants a constructor

        Node(const Type& tp, Node * prev = nullptr, Node * next = nullptr)
           : element(tp)
           , prev(prev)
           , next(next)
        {}

- >What can be simplified

  `push`s and `pop`s. There is common functionality between a special and common cases. Consider for example `push_front`: after the push a new node would become `head`, no matter what:

        push_front(const Type& tp) {
            node = new Node(tp, nullptr, head);
            if (head == 0) {
                tail = node;
            } else {
                head->prev = node;
            }
            head = node;
        }

  Other three function can be similarly streamlined.

- A copy constructor shares too much code with `push_back`. Consider consolidating the common base. Ditto for a destructor and `clear`.

- You may want to assert your invariants: if you have, say, `head == nullptr`, you must also have `tail == nullptr` and `count == 0`.