4

As I understand it, before C++20 iterators were just a concept defined by the standard. Now in C++20 they are real language concepts which are checked at compile time. I'm wondering if I'm safe to assume that I can consume a C++20 iterator in my API and pass it to any pre-C++20 API without it breaking:

Demo

#include <string>
#include <concepts>
#include <cstdio>
#include <utility>

auto write_string(std::input_iterator auto it) -> void { // <-- C++20 iterator

    std::string ret = "Danger";
    std::copy(ret.begin(), ret.end(), it); // <-- requires LegacyInputIterator
}

int main()
{
    std::string str = "Austin is my middle name";
    write_string(str.begin());
    printf("%.*s\n", (int)str.size(), str.data());
}
glades
  • 3,778
  • 1
  • 12
  • 34
  • @康桓瑋 What then is the prefered way to do just what I wrote? Forward the underlying iterator concept used by std::copy to my API? – glades Feb 07 '23 at 13:06
  • 1
    Concepts are a fancy way of checking if the type matches requirements. The actual types are still the same. `decltype(it)` and `decltype(str.begin())` will yield the same type (ignoring the value categories). They are not "convertible", they are the same thing. – Yksisarvinen Feb 07 '23 at 13:15
  • @Yksisarvinen True should have used an input iterator from a differnt kind of entity, but I couldn't think of one (please consider editing the question if you have a more general example). The point is that this API would accept all iterators that satisfy the input_iterator category of c++20. How would I constrain it to the iterators used by std::copy to have a more narrow fit? – glades Feb 07 '23 at 13:18
  • @康桓瑋 This source seems to indicate however that when I'm using c++20 I should in fact use the said concepts to constrain iterators (see guidelines)... – glades Feb 07 '23 at 13:20
  • I'm not sure what your problem is here. If you pass a wrong iterator, it won't compile anyway (maybe with a bit more cryptic error message). `std::vector v{}; write_string(v.begin());` won't compile with or without concepts. You could accept `std::string::iterator` as argument, but that prevents `std::vector::iterator` from being passed. If you want a concept that checks if this specific `copy` can be called, you can devise your own concept that checks if the type is `std::output_iterator` and `std::is_same_v<*it, char>`. But that's a bit over the top IMO. – Yksisarvinen Feb 07 '23 at 13:36

2 Answers2

3

std::copy requires that the type of the third parameter must meet the requirements of LegacyOutputIterator, that is, it must support the writing operation, which is not guaranteed by std::input_iterator.

In C++20, the corresponding concept is std::output_iterator, so you can redeclare write_string as

void write_string(std::output_iterator<const char&> auto it) {
  std::string ret = "Danger";
  std::copy(ret.begin(), ret.end(), it);
}

This guarantees that the type of it satisfies the syntax requirements for output iterators written to char.

(Although the C++20 iterator system is very different from C++98/C++17, the requirements for output iterators are basically equivalent. so in your example, using std::output_iterator to check the requirement of LegacyOutputIterator is fine)

康桓瑋
  • 33,481
  • 5
  • 40
  • 90
0

No.

The old LegacyInputIterator concept also required std::equality_comparable, which is no longer required for std::input_iterator.

Therefore, a C++20 iterator type that's not std::equality_comparable is not a LegacyInputIterator and might break old API's.

std::output_iterator however is not required to be std::equality_comparable.

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • Where is it required that *LegacyIterator* is `equality_comparable`? Cppreference doesn't mention it for [*LegacyIterator*](https://en.cppreference.com/w/cpp/named_req/Iterator) and explicitly mentions that [*LegacyOutputIterator*](https://en.cppreference.com/w/cpp/named_req/OutputIterator) needs not to be comparable. – Yksisarvinen Feb 07 '23 at 13:45
  • @Bob__ Ah, right. It's kinda confusing that OP chose to talk about `std::input_iterator` for argument that should be *LegacyOutputIterator*. I'll take the liberty to edit the answer to change *LegacyIterator* to *LegacyInputIterator* – Yksisarvinen Feb 07 '23 at 14:21