3

Given an std::string s and std::string_view s_view. What is the "best" way to find the distance between the beginning of s_view and s? By "best" I mean what works safely for all of the main C++ compilers?

The two options I can think of are illustrated by the code example below. I am unsure if these two ways of doing it will always work on all main compilers. Also, I can't help but feeling that there is a more "proper" way of doing it, but I couldn't find any std::string or std::string_view methods that would achieve this.

main.cpp

#include <iostream>
#include <string>

int main() {
  std::string s{"0123456789"};
  std::string_view s_view{s.data() + 3, 4};
  std::cout << "Pointer difference: "
            << (s_view.data() - s.data()) << std::endl;
  std::cout << "Iterator distance: "
            << std::distance(std::string_view{s}.begin(), s_view.begin())
            << std::endl;
  return 0;
}

Compiled on a mac with Apple clang version 11.0.0 using clang++ main.cpp -std=c++11. It prints:

Pointer difference: 3
Iterator distance: 3
Jasper Braun
  • 109
  • 8

1 Answers1

3

std::string_view has no concept of the std::string it is viewing. All it knows is the starting char* data pointer it was given.

It is undefined behavior to compare iterators from different containers, even if they refer to the same underlying data.

So, your only option is to compare raw pointers, like you do in your "Pointer difference" example. If you want to compare iterators, you will have to dereference them into pointers first, eg:

std::cout << "Pointer distance via Iterators: "
            << (&*s_view.begin() - &*s.begin())
            << std::endl;

Live Demo

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 1
    While I definitely agree with the answer, could you provide the exact Standard Section that claims that it's illegal to compare iterators from different containers? – Fureeish Aug 31 '20 at 22:37
  • 1
    @Fureeish https://stackoverflow.com/questions/4657513/ – Remy Lebeau Sep 01 '20 at 00:51
  • 1
    The answer talks about sequences. Why would we assume they are identical to the concept of containers? In the original question, I'd say we are dealing with the same sequence of elements, but accesses with different containers / views (are views separate from containers?). Is it still wrong? – Fureeish Sep 01 '20 at 01:30
  • 1
    @Fureeish An iterator is specific to the container it comes from. Even if the underlying memory behind the iterator is shared, an iterator from one container can't be an iterator in another container. Comparing iterators from different containers is undefined. I think that is covered by `§ 24.2.1` in [this answer](https://stackoverflow.com/a/4664519/65863): "*An iterator `j` is called reachable from an iterator `i` if and only if there is a finite sequence of applications of the expression `++i` that makes `i == j`. If `j` is reachable from `i`, they refer to elements of the same sequence.*" – Remy Lebeau Sep 01 '20 at 01:44
  • 1
    @Fureeish Also see [C++ Standard Library Closed Issues List](http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-closed.html), [#446 "Iterator equality between different containers"](http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-closed.html#446) – Remy Lebeau Sep 01 '20 at 01:45
  • Does the "*subranges*" in the **Proposed resolution:** mean ``'s `subrane` or does it have a different meaning? Can't find the exact definition from the standard. I could see how we could use two `std::string_view`s to point to the same chunk of text and call both of them *subranges*, but your answer hints that comparing those iterators would be UB. This seems tricky for me (likewise for the people who contributed to the resolved issue you linked, as it seems). – Fureeish Sep 01 '20 at 09:53