fromgate
fromgate

Reputation: 21

How to make a copy constructor for different types within a template class?

I need to make my Iterator< isConst = false> convert to Iterator<isConst = true>. That is, I need a separate method Iterator< true >(const Iterator< false > &).

My Iterator class:

template < typename T >
template < bool isConst >
class ForwardList< T >::Iterator
{
  using value_type = std::conditional_t< isConst, const T, T >;
  using difference_type = ptrdiff_t;
  using pointer = std::conditional_t< isConst, const T *, T * >;
  using reference = std::conditional_t< isConst, const T &, T & >;
  using iterator_category = std::forward_iterator_tag;

  friend class ForwardList< T >;
private:
  explicit Iterator(node_t *nodePtr): nodePtr_(nodePtr) {}
public:
  Iterator() = default;
  Iterator(const Iterator &other) = default;
  ~Iterator() = default;

  reference operator*() const;
  pointer operator->() const;
  Iterator &operator++();
  Iterator operator++(int) &;
  bool operator==(const Iterator &other) const;
  bool operator!=(const Iterator &other) const;

private:
  node_t *nodePtr_;
};

I tried overloading the copy constructor and specializing the template. I understand that if you split the Iterator into two classes, it can be done, but I don't want to duplicate so much code.

Upvotes: 0

Views: 504

Answers (2)

fabian
fabian

Reputation: 82461

You can make the iterator constructor that accepts iterators with arbitrary template parameters, unless the constructed iterator is a non-const iterator and the constructor parameter is a const iterator:

template < typename T >
template < bool isConst >
class ForwardList< T >::Iterator
{
...

    friend class ForwardList<T>::Iterator<!isConst>;
public:
    Iterator() = default;

    template<bool otherIsConst, std::enable_if_t<isConst || !otherIsConst, int> = 0>
    Iterator(Iterator<otherIsConst> const& other)
        : nodePtr_(other.nodePtr_)
    {
    }

    ~Iterator() = default;
    ...
private:
    node_t* nodePtr_ {nullptr}; // note: crash more likely for dereferencing the default-constructed object
};

static_assert(std::is_constructible_v<ForwardList<int>::Iterator<true>, ForwardList<int>::Iterator<true> const&>, "expected constructor unavailable");
static_assert(std::is_constructible_v<ForwardList<int>::Iterator<true>, ForwardList<int>::Iterator<false> const&>, "expected constructor unavailable");
static_assert(std::is_constructible_v<ForwardList<int>::Iterator<false>, ForwardList<int>::Iterator<false>const&>, "expected constructor unavailable");
static_assert(!std::is_constructible_v<ForwardList<int>::Iterator<false>, ForwardList<int>::Iterator<true> const&>, "unexpected constructor available");

Upvotes: 0

Caleth
Caleth

Reputation: 62704

Rather than having a constructor that takes an Iterator<false>, you can have a conversion operator that returns an Iterator<true>.

operator Iterator<true>() const { return Iterator<true>(nodePtr_); }

You will need to friend class Iterator<false>; to access your private constructor.

See it live

Upvotes: 3

Related Questions