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.