vector<unique_ptr> doesn't have a Random Access Iterator?

By Gašper Ažman

The good book says:

27.2.3 Input Iterators:

Expression Return type Semantics
*r convertible to T
*r++ convertible to T { T tmp = *r; ++r; return tmp; }

RandomAccessIterator inherits these.

What is

It's False.

(well technically it's derived from std::false_type, but close enough)

 is_convertible<unique_ptr<int>&, unique_ptr<int>>{}?

... When you are writing a concepts library ...

error: requirement

Convertible(unique_ptr<int>&, unique_ptr<int>)

not satisfied.

required by InputIterator(vector<unique_ptr<int>>::iterator)

required by ForwardIterator(vector<unique_ptr<int>>::iterator)

required by BidirectionalIterator(vector<unique_ptr<int>>::iterator)

required by RandomAccessIterator(vector<unique_ptr<int>>::iterator)

vector<unique_ptr<int>> v;
v.push_back(make_unique(5));
for_each(v.begin(), v.end(), [](auto&) {});

What's the big deal?

T& | T const& | T | T&& | proxy

T& is not convertible to T for move-only types.

AKA vector<unique_ptr>::iterator

What's the big deal?

T& | T const& | T | T&& | proxy

NOPE

AKA vector<unique_ptr>::const_iterator

What's the big deal?

T& | T const& | T | T&& | proxy

ACTUALLY WORKS!

... with the caveat that:

- there are no such iterators (yet)

- they would move the original values out

What's the big deal?

T& | T const& | T | T&& | proxy

ACTUALLY WORKS!

FOR REAL

AKA move_iterator<vector<unique_ptr>::iterator>

But remember *r++?

{ T tmp = *r;
  ++r;
  return tmp; }

The operational semantics ALWAYS MOVE!

even if you're discarding the value.

What's the big deal?

T& | T const& | T | T&& | proxy

May... or may not.

AKA vector<unique_ptr>{} | filter{predicate}

But remember *r++?

{ T tmp = *r;
  ++r;
  return tmp; }

SAME PROBLEM... Maybe. Or plain doesn't work.

The fix

The good book should say:

27.2.3 Input Iterators:

Expression Return type Semantics
*r convertible to T const&
*r++ convertible to T const& { MAGIC tmp = *r;
  ++r;
  return tmp; }

MAGIC is a problem because...

*r and *r++ may return:

- T

- T const&

- T&

- T&& 

FIN.

Because explaining magic is a whole other talk.

 

Find me after if interested.

Working through MAGIC: 

T | proxy| T& | T const& | T&&

*r

*r++

Persistent values (vector)

Generators

Binds to T const&!

{
    auto tmp = *r; // move or copy
    ++r;
    return tmp;    // move
} -> T
{
    auto tmp = *r; // move or copy
    ++r;
    return tmp;    // move
} -> T
using T = std::unique_ptr<int>;
struct proxy {
  operator T() const {
     return make_unique<int>(0);
  }
};

struct iterator {
  proxy operator*() const {
    return proxy{};
  }
};

What if *r is a proxy that converts to T?

Yup, works.

#include <type_traits>
#include <memory>

using T = std::unique_ptr<int>;
struct proxy {
  operator T() const { return std::make_unique<int>(0); }
};

struct iterator {
  proxy operator*() const {
    return proxy{};
  }
};

int main() {
  iterator x;
  T const& y = *x;
  static_assert(std::is_convertible<decltype(*x),
                                    T const&>::value, "");
}

Potential issues

What if *r is a T&?

T& binds to T const&.

 

 

 

If you need that, you are asking for a mutable iterator, not an InputIterator, and have an extra requirement.

And now for *r++

Input iterators where *r returns a value type

Operational semantics of *r++

{

  T tmp = *r;

  ++r;

  return tmp;

}

move

move

The move works because we are not converting.

 

how do we ask for T const& or move constructible?

T

Current wording

Potential issues

Input iterators where *r returns a T const&

Operational semantics of *r++

{

  MAGIC tmp = *r;

  ++r;

  return tmp;

}

{

  T tmp = *r;

  ++r;

  return tmp;

}

T

MAGIC needs to be convertible to

T const&, and persist the object IF the iterator doesn't hang on to it.

MAGIC

Current wording

What we need

Potential issues

Input iterators where *r returns a T&

Operational semantics of *r++

{

  MAGIC tmp = *r;

  ++r;

  return tmp;

}

{

  T tmp = *r;

  ++r;

  return tmp;

}

MAGIC

T

MAGIC needs to be convertible to

T const&, persist the object if necessary, but we also need convertibility to T&&

Current wording

What we need

Potential issues

Input iterators where *r returns a T&&

Operational semantics of *r++

{

  MAGIC tmp = *r;

  ++r;

  return tmp;

}

{

  T tmp = *r;

  ++r;

  return tmp;

}

Is T&&

T

This *always* moves

MAGIC needs to persist if necessary but convert to T&&, which converts to

T const&.

MAGIC

We only want to move IF ACTUALLY MOVING

We need help with MAGIC definition, but the current one is not good enough.

Conclusion

I also want holes poked into the T const& convertibility, but currently I don't see any.

Potential issues

Algorithms relying on convertibility to T?

There are none. All of them specify convertibility in addition already. (<numeric>, <algorithm> survey done)

deck

By Gašper Ažman