I am trying to define an iterator that iterates through a puzzle game board cells by a given starting cell and some link representing some kind of a connection between the cells. Below I provided a sample code with the same methods but a trivial implementation that potentially should iterate over the cells with the coordinates {0, 0}, {1, 1}, {2, 2}, {3, 3}, {4, 4}
:
#include <ranges>
struct CellCoord { size_t col; size_t row; };
struct NeighborIteratorSentinel {};
class NeighborIterator
{
public:
NeighborIterator(size_t link_id) :
linkId(link_id)
{}
using iterator_category = std::forward_iterator_tag;
using value_type = CellCoord;
using difference_type = std::ptrdiff_t;
using pointer = value_type*;
using reference = value_type&;
CellCoord operator-> () const { return cur(); }
CellCoord operator* () const { return cur(); }
NeighborIterator& operator++ ()
{
++linkId;
return *this;
}
NeighborIterator operator++ (int)
{
NeighborIterator tmp = *this;
++linkId;
return tmp;
}
NeighborIterator(const NeighborIterator& other) = default;
NeighborIterator(NeighborIterator&& other) = default;
NeighborIterator& operator = (const NeighborIterator& other) = default;
NeighborIterator& operator = (NeighborIterator&& other) = default;
NeighborIterator& operator = (const NeighborIteratorSentinel&)
{
linkId = linkCount;
}
NeighborIterator& operator = (NeighborIteratorSentinel&&)
{
linkId = linkCount;
}
bool operator == (const NeighborIterator& r) const noexcept
{
return linkId == r.linkId;
}
bool operator != (const NeighborIterator& r) const noexcept
{
return !operator == (r);
}
bool operator == (const NeighborIteratorSentinel&) const noexcept
{
return linkId == linkCount;
}
bool operator != (const NeighborIteratorSentinel& r) const noexcept
{
return !operator == (r);
}
protected:
CellCoord cur() const
{
return {linkId, linkId};
}
private:
size_t linkId;
static constexpr size_t linkCount = 5;
};
The problem is that I can't compile the following method:
inline auto MakeNeighborRange()
{
return std::ranges::subrange<NeighborIterator, NeighborIteratorSentinel>(NeighborIterator(0u), NeighborIteratorSentinel{});
}
because std::sentinel_for
constraint is not satisfied.
How to make this compile? What does this constraint want of me?
MSVC output:
C:\dev\temp>cl /std:c++20 /EHsc NeighborIterator.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.39.33523 for x86
Copyright (C) Microsoft Corporation. All rights reserved.
NeighborIterator.cpp
NeighborIterator.cpp(101): error C7602: 'std::ranges::subrange': the associated constraints are not satisfied
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\xutility(3735): note: see declaration of 'std::ranges::subrange'
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\xutility(3733): note: the concept 'std::sentinel_for<example::NeighborIteratorSentinel,example::NeighborIterator>' evaluated to false
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\__msvc_iter_core.hpp(421): note: the concept 'std::_Weakly_equality_comparable_with<example::NeighborIteratorSentinel,example::NeighborIterator>' evaluated to false
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\concepts(193): note: the concept 'std::_Half_equality_comparable<example::NeighborIteratorSentinel,example::NeighborIterator>' evaluated to false
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\concepts(187): note: 'bool example::NeighborIterator::operator ==(const example::NeighborIteratorSentinel &) noexcept const': rewritten candidate function was excluded from overload resolution because a corresponding operator!= declared in the same scope
NeighborIterator.cpp(75): note: could be 'bool example::NeighborIterator::operator ==(const example::NeighborIteratorSentinel &) noexcept const' [synthesized expression 'y == x']
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\concepts(187): note: 'bool example::NeighborIterator::operator ==(const example::NeighborIteratorSentinel &) noexcept const': rewritten candidate function was excluded from overload resolution because a corresponding operator!= declared in the same scope
NeighborIterator.cpp(65): note: or 'bool example::NeighborIterator::operator ==(const example::NeighborIterator &) noexcept const' [synthesized expression 'y == x']
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\concepts(187): note: 'bool example::NeighborIterator::operator ==(const example::NeighborIterator &) noexcept const': cannot convert argument 2 from 'const example::NeighborIteratorSentinel' to 'const example::NeighborIterator &'
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\concepts(187): note: Reason: cannot convert from 'const example::NeighborIteratorSentinel' to 'const example::NeighborIterator'
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\concepts(187): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\xstring(5047): note: or 'bool std::operator ==(const std::basic_string<_Elem,_Traits,_Alloc> &,const _Elem *const ) noexcept'
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\xstring(5041): note: or 'bool std::operator ==(const std::basic_string<_Elem,_Traits,_Alloc> &,const std::basic_string<_Elem,_Traits,_Alloc> &) noexcept'
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\xstring(1661): note: or 'bool std::operator ==(const std::basic_string_view<_Elem,_Traits>,const _Identity<std::basic_string_view<_Elem,_Traits>>::type) noexcept'
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\xstring(1647): note: or 'bool std::operator ==(const std::basic_string_view<_Elem,_Traits>,const std::basic_string_view<_Elem,_Traits>) noexcept'
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\xmemory(1042): note: or 'bool std::operator ==(const std::allocator<_Ty> &,const std::allocator<_Other> &) noexcept'
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\tuple(872): note: or 'bool std::operator ==(const std::tuple<_Types...> &,const std::tuple<_Types...> &)'
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\iterator(488): note: or 'bool std::operator ==(const std::istreambuf_iterator<_Elem,_Traits> &,const std::istreambuf_iterator<_Elem,_Traits> &)'
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\iterator(306): note: or 'bool std::operator ==(const std::istream_iterator<_Ty,_Elem,_Traits,_Diff> &,const std::istream_iterator<_Ty,_Elem,_Traits,_Diff> &) noexcept'
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\xutility(4228): note: or 'bool std::operator ==(const std::move_iterator<_Iter> &,const std::move_iterator<_Iter2> &) noexcept(<expr>)'
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\xutility(1686): note: or 'bool std::operator ==(const std::reverse_iterator<_BidIt> &,const std::reverse_iterator<_BidIt2> &) noexcept(<expr>)'
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\utility(490): note: or 'bool std::operator ==(const std::pair<_Ty1,_Ty2> &,const std::pair<_Uty1,_Uty2> &)'
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\utility(490): note: or 'bool std::operator ==(const std::pair<_Ty1,_Ty2> &,const std::pair<_Uty1,_Uty2> &)' [synthesized expression 'y == x']
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\xutility(1686): note: or 'bool std::operator ==(const std::reverse_iterator<_BidIt> &,const std::reverse_iterator<_BidIt2> &) noexcept(<expr>)' [synthesized expression 'y == x']
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\xutility(4228): note: or 'bool std::operator ==(const std::move_iterator<_Iter> &,const std::move_iterator<_Iter2> &) noexcept(<expr>)' [synthesized expression 'y == x']
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\iterator(306): note: or 'bool std::operator ==(const std::istream_iterator<_Ty,_Elem,_Traits,_Diff> &,const std::istream_iterator<_Ty,_Elem,_Traits,_Diff> &) noexcept' [synthesized expression 'y == x']
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\iterator(488): note: or 'bool std::operator ==(const std::istreambuf_iterator<_Elem,_Traits> &,const std::istreambuf_iterator<_Elem,_Traits> &)' [synthesized expression 'y == x']
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\tuple(872): note: or 'bool std::operator ==(const std::tuple<_Types...> &,const std::tuple<_Types...> &)' [synthesized expression 'y == x']
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\xmemory(1042): note: or 'bool std::operator ==(const std::allocator<_Ty> &,const std::allocator<_Other> &) noexcept' [synthesized expression 'y == x']
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\xstring(1647): note: or 'bool std::operator ==(const std::basic_string_view<_Elem,_Traits>,const std::basic_string_view<_Elem,_Traits>) noexcept' [synthesized expression 'y == x']
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\xstring(1661): note: or 'bool std::operator ==(const std::basic_string_view<_Elem,_Traits>,const _Identity<std::basic_string_view<_Elem,_Traits>>::type) noexcept' [synthesized expression 'y == x']
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\xstring(5041): note: or 'bool std::operator ==(const std::basic_string<_Elem,_Traits,_Alloc> &,const std::basic_string<_Elem,_Traits,_Alloc> &) noexcept' [synthesized expression 'y == x']
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\xstring(5047): note: or 'bool std::operator ==(const std::basic_string<_Elem,_Traits,_Alloc> &,const _Elem *const ) noexcept' [synthesized expression 'y == x']
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\concepts(187): note: while trying to match the argument list '(const example::NeighborIteratorSentinel, const example::NeighborIterator)'
NeighborIterator.cpp(101): error C2641: cannot deduce template arguments for 'std::ranges::subrange'
NeighborIterator.cpp(101): error C2783: 'std::ranges::subrange<_It,_Se,_Ki> std::ranges::subrange(std::true_type,_Rng &&)': could not deduce template argument for '_It'
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\xutility(3745): note: see declaration of 'std::ranges::subrange'
NeighborIterator.cpp(101): error C2780: 'std::ranges::subrange<_It,_Se,_Ki> std::ranges::subrange(std::ranges::subrange<_It,_Se,_Ki>)': expects 1 arguments - 2 provided
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\xutility(3735): note: see declaration of 'std::ranges::subrange'
This also does not compile:
inline auto MakeNeighborRange()
{
return std::ranges::subrange<NeighborIterator>(NeighborIterator(0u), NeighborIterator(0u));
}
MSVC output:
C:\dev\temp>cl /std:c++20 /EHsc NeighborIterator.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.39.33523 for x86
Copyright (C) Microsoft Corporation. All rights reserved.
NeighborIterator.cpp
NeighborIterator.cpp(103): error C7602: 'std::ranges::subrange': the associated constraints are not satisfied
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\xutility(3735): note: see declaration of 'std::ranges::subrange'
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\xutility(3733): note: the concept 'std::sentinel_for<example::NeighborIterator,example::NeighborIterator>' evaluated to false
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\__msvc_iter_core.hpp(419): note: the concept 'std::semiregular<example::NeighborIterator>' evaluated to false
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\concepts(255): note: the concept 'std::default_initializable<example::NeighborIterator>' evaluated to false
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\concepts(98): note: the concept 'std::constructible_from<example::NeighborIterator,>' evaluated to false
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\concepts(95): note: the constraint was not satisfied
NeighborIterator.cpp(103): error C2641: cannot deduce template arguments for 'std::ranges::subrange'
NeighborIterator.cpp(103): error C2783: 'std::ranges::subrange<_It,_Se,_Ki> std::ranges::subrange(std::true_type,_Rng &&)': could not deduce template argument for '_It'
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\xutility(3745): note: see declaration of 'std::ranges::subrange'
NeighborIterator.cpp(103): error C2780: 'std::ranges::subrange<_It,_Se,_Ki> std::ranges::subrange(std::ranges::subrange<_It,_Se,_Ki>)': expects 1 arguments - 2 provided
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include\xutility(3735): note: see declaration of 'std::ranges::subrange'
adding a default constructor fixes this:
NeighborIterator() : NeighborIterator(0) {}
but the first implementation of MakeNeighborRange()
still does not compile.
Customized bool NeighborIterator::operator!=(const NeighborIteratorSentinel&)
will make NeighborIteratorSentinel{} != NeighborIterator{}
ll-formed as the compiler does not synthesize the corresponding operator!=
in this case, this causes sentinel_for
constraints to be unsatisfied.
In C++20, you don't need to define operator!=
, just define operator==
. The compiler will synthesize all corresponding (in) equality operators for you.
In other words, the simple workaround is to remove operator!=
.