Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::upper_bound returns const iterator in const member function

Here is a class that contains a boost::circular_buffer of some struct. I make a typedef for iterators into the contained circular_buffer.

My problem is this: when the doWork function is marked const, the returned value of std::upper_bound is not compatible with the MyIterator type due to the return value having boost::cb_details::const_traits. If I remove the const keyword from the function, all my compile errors go away.

To be clear the compiler error is this:

error: conversion from ‘boost::cb_details::iterator<boost::circular_buffer<Wrapper<int>::Sample, std::allocator<Wrapper<int>::Sample> >, boost::cb_details::const_traits<std::allocator<Wrapper<int>::Sample> > >’ to non-scalar type ‘Wrapper<int>::MyIterator {aka boost::cb_details::iterator<boost::circular_buffer<Wrapper<int>::Sample, std::allocator<Wrapper<int>::Sample> >, boost::cb_details::nonconst_traits<std::allocator<Wrapper<int>::Sample> > >}’ requested    
                          [](const Sample& a, const Sample& b) { return a.foo < b.foo; });

Here is a self-contained example:

#include <algorithm>
#include <boost/circular_buffer.hpp>

template <typename T>
class Wrapper {
 public:
    struct Sample {
        T foo;
    };

    typedef typename boost::circular_buffer<Sample>::iterator MyIterator;

    Wrapper(int size) { cb.resize(size); }

    void add(T val) { cb.push_back(Sample{val}); }

    void doWork(T bound) const {
        MyIterator iter =
            std::upper_bound(cb.begin(), cb.end(), Sample{3},
                         [](const Sample& a, const Sample& b) { return a.foo < b.foo; });
    }

    boost::circular_buffer<Sample> cb;
};

int main() {
    Wrapper<int> buf(100);
    buf.add(1);
    buf.add(5);
    buf.doWork(3);
    return 0;
}

So, why can't this function be const? Why does marking it const have this side-effect? I want a non-const iterator into the container, but in my real test case I don't intend to actually modify the container at all.

like image 609
Chris Avatar asked Aug 30 '16 22:08

Chris


2 Answers

You're going to need a const_iterator, since you're effectively observing a const container.

Perhaps:

typedef typename boost::circular_buffer<Sample>::const_iterator MyConstIterator;

… then make iter one of these.

Someone's going to tell you that you could have avoided this with auto. That's true, but then you never would have discovered this "bug", or that const_iterators exist.

like image 172
Lightness Races in Orbit Avatar answered Nov 03 '22 21:11

Lightness Races in Orbit


If your function is marked const then all your access to member variables will be const too.

A const container will only allow access to const_ iterators, that's just the way iterators work.

like image 40
Mark Ransom Avatar answered Nov 03 '22 19:11

Mark Ransom