2

My class (let's call it A) takes in a string::iterator and it implements two methods:

  • auto peek() const -> const char &: Returns a reference to the data at the current position.
  • auto get() -> const char &: Returns a reference to the data at the current position then increments the position.

The solution I have come up with is the following:

class A {
  std::string::iterator _s;

public:
  A() = delete;

  explicit A(std::string::iterator s) : _s(s) {}

  auto peek() const -> const char & {
    return *_s;
  }

  auto get() -> const char & {
    return *(_s++);  // <-- valid?
  }
};

In this case, I understand returning a temporary copy of the value would also work since char is small enough in size:

auto get2() -> char {
  const auto c = *_s;
  _s++;
  return c;
}

However, say the data was sufficiently large enough such that I wanted to return a reference instead of copying. Would the following code be a valid way of doing this in C++?

auto get() -> const char & {
  return *(_s++);
}
nik
  • 27
  • 1
  • 6
  • 3
    Be careful about variable names starting with underscores: https://stackoverflow.com/a/25090719/1643973 – jjramsey Jul 02 '21 at 19:13
  • 2
    Do note you don't have a good way of checking `_s` against `string.end()`. Also maybe considering using `string::const_iterator` instead of `string::iterator` – Ranoiaetep Jul 02 '21 at 20:32
  • @jjramsey thank you I was not aware of this until now. Will keep in mind going forward! – nik Jul 03 '21 at 14:17
  • @Ranoiaetep yes those are some good tips, thank you! I have since made it a `const_iterator` and checked for `end()`. – nik Jul 03 '21 at 14:19

1 Answers1

3

Yes, return *(_s++); is perfectly valid and safe, provided s is a valid iterator to begin with (ie, the string is alive, and s is within the string's valid iterator range).

_s++ will increment s, returning a new iterator that is a copy of s before it was incremented.

Then * dereferences that copied iterator, yielding a non-const char& reference to the char that s originally referred to within the string.

You then return that reference as-is to the caller as a const char& reference, which is perfectly fine.

Even if you did not want to trust this logic, your approach to use a local variable is fine, and you can return a reference by simply declaring that variable as a reference rather than a value, eg:

auto get() -> const char & {
  const char &c = *_s;
  ++_s;
  return c;
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • The key part being that the temporary isn't the char, it's the iterator. My mistake came from missing that detail. – YSC Jul 02 '21 at 19:11
  • 1
    Perfectly valid and safe for string and any other BidirectionalIterator or ForwardIterator. The answer isn't valid for an InputIterator (the described sequence of operations would not be safe, InputIterator postincrement has to jump through extra hoops to be safe). – Ben Voigt Jul 02 '21 at 20:27
  • @BenVoigt an InputIterator is required to support the `*i++` operation with expected results. What it does internally to accomplish that is its own business – Remy Lebeau Jul 02 '21 at 20:42