CaseyCarter/cmcstl2

Weird SizedRange compilation problem

Opened this issue · 2 comments

The following code shows a compilation problem:

#include <experimental/ranges/concepts>
#include <experimental/ranges/ranges>
#include <iostream>
#include <iterator>
#include <string>

/** Class to allow iteration over all lines from an input stream. */
class istream_line_reader {
public:
    /**
     * Iterator that contains the line content.
     *
     * The iterator \e owns the content.
     */
    class iterator  // implements InputIterator
    {
    public:
        typedef int                     difference_type;
        typedef std::string             value_type;
        typedef value_type*             pointer_type;
        typedef value_type&             reference;
        typedef std::input_iterator_tag iterator_category;

        iterator() : _M_stream(nullptr) {}
        explicit iterator(std::istream& is) : _M_stream(&is)
        {
            ++*this;
        }

        reference operator*()
        {
            assert(_M_stream != nullptr);
            return _M_line;
        }
        pointer_type operator->()
        {
            assert(_M_stream != nullptr);
            return &_M_line;
        }
        iterator& operator++()
        {
            assert(_M_stream != nullptr);
            getline(*_M_stream, _M_line);
            if (!*_M_stream)
                _M_stream = nullptr;
            return *this;
        }
        iterator operator++(int)
        {
            iterator temp(*this);
            ++*this;
            return temp;
        }

        bool operator==(const iterator& rhs) const
        {
            return _M_stream == rhs._M_stream;
        }
        bool operator!=(const iterator& rhs) const
        {
            return !operator==(rhs);
        }

    private:
        std::istream* _M_stream;
        std::string   _M_line;
    };

    istream_line_reader() noexcept
        : _M_stream(nullptr)
    {
    }
    explicit istream_line_reader(std::istream& is) noexcept
        : _M_stream(&is)
    {
    }
    iterator begin()
    {
        return iterator(*_M_stream);
    }
    iterator end() const
    {
        return iterator();
    }

private:
    std::istream* _M_stream;
};

#define TEST_CONCEPT(Concept, Type...) \
    cout << #Concept << '<' << #Type << ">: " << Concept<Type> << endl

int main()
{
    namespace ranges = std::experimental::ranges;
    using ranges::Range;
    using ranges::View;
    using ranges::SizedRange;
    using std::cout;
    using std::endl;
    cout << std::boolalpha;
    auto line_reader = istream_line_reader(std::cin);
    auto rng = line_reader | ranges::view::take(5);
    TEST_CONCEPT(Range, decltype(line_reader));
    TEST_CONCEPT(View, decltype(line_reader));
    TEST_CONCEPT(SizedRange, decltype(line_reader));
    TEST_CONCEPT(Range, decltype(rng));
    TEST_CONCEPT(View, decltype(rng));
    TEST_CONCEPT(SizedRange, decltype(rng));  // DOESN'T COMPILE
    for (auto& line : rng) {
        cout << line << endl;
    }
}

I intend to check which concepts are satisfied. However, the line TEST_CONCEPT(SizedRange, decltype(rng)); (marked above) does not compile. I expect to see it compile and output false at run time.

Which one is at fault, my code, CMCSTL2, or GCC? I doubt it is GCC this time, but I would like to hear experts’ opinions.

The problem is gone after I add const to the definition of pointer_type, reference, operator*, and operator->() of istream_line_reader::iterator (thanks to Eric’s help):

        typedef const value_type*       pointer_type;
        typedef const value_type&       reference;
…
        reference operator*() const
        {
            assert(_M_stream != nullptr);
            return _M_line;
        }
        pointer_type operator->() const
        {
            assert(_M_stream != nullptr);
            return &_M_line;
        }

It still feels strange why checking concept could cause compilation errors. . . .

You are right; it shouldn't be a hard error. I had overlooked that part of your bug report. I or @CaseyCarter will investigate.