Home > Enterprise >  msvc std::lower_bound requires const operator*
msvc std::lower_bound requires const operator*

Time:03-19

I am getting a

Error   C2678   binary '*': no operator found which takes a left-hand operand of type 'const _InIt' (or there is no acceptable conversion)

it is thrown by this code in MSVC (2022, V17.1) <algorithm> header.

template <class _FwdIt, class _Ty, class _Pr>
_NODISCARD _CONSTEXPR20 _FwdIt lower_bound(_FwdIt _First, const _FwdIt _Last, const _Ty& _Val, _Pr _Pred) {

...
        const auto _UMid                   = _STD next(_UFirst, _Count2);
        if (_Pred(*_UMid, _Val)) { // try top half

The second line throws the error because of the const on the line above.

The iterator I am passing in, is a custom LegacyRandomAccessIterator over a "flat-file" and its operator* looks like this:

  value_type operator*() { return current(); }

...

  ValueType current() {
    if (!cur_valid_) {
      cur_       = ffdb_->get_record(pos_);
      cur_valid_ = true;
    }
    return cur_;
  }

In other words, I can't just mark my operator* as const because it's not, and can't be really - It's doing loading of a buffered set of records from disk, and that's not a const operation in a the current design.

I implemented a "dummy" const version to prove that was the problem:

 value_type operator*() const { return value_type{}; }

Error is gone, but clearly this doesn't do anything.

libstdc doesn't have this expectation of a const operator*. (I haven't tested libc )

Is this solvable, without some major redesign of my iterator?

Is it reasonable for MSVC's implementation to have this expectation?

CodePudding user response:

std::lower_bound takes a Cpp17ForwardIterator, which must also be a Cpp17InputIterator. The Cpp17InputIterator requirements include:

Expression Return type
*a reference, convertible to T

Here, a is a "value of type X or const X", so MSVC is justified in requiring a const-qualified unary indirection operator; the "or" means that the code using the iterator can use either, and the author of the iterator has to support both. (Note that Cpp17InputIterator differs from Cpp17OutputIterator, where the required operation is *r = o, with r a non-const reference, X&.)

So your operator* should have const qualification, and return a reference; specifically, a reference to T or const T (this is a Cpp17ForwardIterator requirement). You can satisfy this straightforwardly with using reference = const T& and by making cur_ and cur_valid_ mutable.

The use of mutable here is entirely legitimate; since operator*() const is idempotent, it is "logically const" and the modifications to the data members are non-observable.

  • Related