typename List<T>::iterator List<T>::erase_after(typename List<T>::const_iterator itr) {
  Node *temp = (itr.ptr())->next; // apontando para o que vai ser deletado
  (itr.ptr())->next = temp->next;
  temp->next->last = itr.ptr();
  delete temp;
  this->m_size--;
  itr++;
  List<T>::iterator retorno = itr.ptr();
  return retorno;
}
typename List<T>::const_iterator List<T>::insert_after(typename List<T>::const_iterator itr, const T &x) {
  Node *temp = new Node(); // criando o novo nó
  temp->data = x;
  temp->next = (itr.ptr())->next; // esse novo nó aponta para o que vem depois do nó atraz dele
  temp->last = itr.ptr();
  (itr.ptr())->next->last = temp; // muito complexo, melhor não comentar
  (itr.ptr())->next = temp; // o que está atraz agora aponta pra ele
  this->m_size++;
  itr++;
  return itr;
}
typename List<T>::const_iterator List<T>::find(const T &x) const {
  typename List<T>::const_iterator temp(this->m_head.next); // começa pelo primeiro
  while(temp.ptr() != nullptr) { // e não para até achar
    if(*temp == x) return temp;
    else temp++;
  } // ou não achar
  throw std::runtime_error("Valor não encontrado"); // eue não encontrei então não posso retornar
}
typename List<T>::iterator List<T>::erase_after(typename List<T>::const_iterator first , typename List<T>::const_iterator last ) {
  Node *temp = (first.ptr())->next; // apontando para o que vai ser deletado
  (first.ptr())->next = last.ptr();
  (last.ptr())->last = first.ptr();
  first++;
  while(first != last) { // a sobrecarga de operador é util aqui
    first++;
    delete temp;
    temp = first.ptr();
    this->m_size--; // cada vez que deleta diminui um
  }
  first++; // andando um no
  List<T>::iterator retorno = first.ptr(); //convertendo de const para iterator
  return retorno;
}