Search code examples
c++replacec++20projectionc++23

replace range of projected struct fields


I have a range over a multi field structure.

The fields can only be accessed via operator [].

I want to run a replace over one of these fields.

I can do this using a range based for loop.

Is there a better way using a range::replace() function ? I am using C++20, but I am also curious if that can be done using C++23 if needed.

#include <ranges>
#include <algorithm>
using namespace std;

enum Direction
{
    HORIZONTAL,
    VERTICAL
};

struct Maille
{
    int16_t i, j;

    int16_t& operator[](Direction input_direction)
    {
        switch (input_direction)
        {
        case HORIZONTAL:
            return j;
        case VERTICAL:
            return i;
        }
    }
    int16_t operator[](Direction input_direction) const
    {
        switch (input_direction)
        {
        case HORIZONTAL:
            return j;
        case VERTICAL:
            return i;
        }
    }
};

int main()
{
    
    Maille tab[2]={
        {.i=1,.j=2},
        {.i=2,.j=1}
    };
    
    for (Maille& m : tab)
    {
        if (m[HORIZONTAL]==1) 
            m[HORIZONTAL]=4;
    }
    
    ranges::replace(tab | views::transform([](Maille& m){
        return m[HORIZONTAL];}),1,4);


    return 0;
}

Error message:

main.cpp: In function ‘int main()’:
main.cpp:52:20: error: no match for call to ‘(const std::ranges::__replace_fn) (std::ranges::transform_view, main():: >, int, int)’
   52 |     ranges::replace(tab | views::transform([](Maille& m){
      |     ~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   53 |         return m[HORIZONTAL];}),1,4);
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/c++/14/algorithm:63,
                 from main.cpp:3:
/usr/include/c++/14/bits/ranges_algo.h:782:7: note: candidate: ‘template  requires (input_iterator<_Iter>) && (sentinel_for<_Sent, _Iter>) && ((indirectly_writable<_Iter, const _Tp2&>) && (indirect_binary_predicate::__type, const _Tp1*>)) constexpr _Iter std::ranges::__replace_fn::operator()(_Iter, _Sent, const _Tp1&, const _Tp2&, _Proj) const’
  782 |       operator()(_Iter __first, _Sent __last,
      |       ^~~~~~~~
/usr/include/c++/14/bits/ranges_algo.h:782:7: note:   candidate expects 4 arguments, 3 provided
/usr/include/c++/14/bits/ranges_algo.h:799:7: note: candidate: ‘template  requires (input_range<_Range>) && ((indirectly_writable)())), const _Tp2&>) && (indirect_binary_predicate)())), _Proj>::__type, const _Tp1*>)) constexpr std::ranges::borrowed_iterator_t<_Range> std::ranges::__replace_fn::operator()(_Range&&, const _Tp1&, const _Tp2&, _Proj) const’
  799 |       operator()(_Range&& __r,
      |       ^~~~~~~~
/usr/include/c++/14/bits/ranges_algo.h:799:7: note:   template argument deduction/substitution failed:
/usr/include/c++/14/bits/ranges_algo.h:799:7: note: constraints not satisfied
In file included from /usr/include/c++/14/bits/stl_iterator_base_types.h:71,
                 from /usr/include/c++/14/iterator:61,
                 from /usr/include/c++/14/ranges:43,
                 from main.cpp:2:
/usr/include/c++/14/bits/iterator_concepts.h: In substitution of ‘template<class _Range, class _Tp1, class _Tp2, class _Proj>  requires (input_range<_Range>) && ((indirectly_writable<decltype(std::ranges::__access::__begin((declval<_Container&>)())), const _Tp2&>) && (indirect_binary_predicate<std::ranges::equal_to, typename std::__detail::__projected<decltype(std::ranges::__access::__begin((declval<_Container&>)())), _Proj>::__type, const _Tp1*>)) constexpr std::ranges::borrowed_iterator_t<_Range> std::ranges::__replace_fn::operator()(_Range&&, const _Tp1&, const _Tp2&, _Proj) const [with _Range = std::ranges::transform_view<std::ranges::ref_view<Maille [2]>, main()::<lambda(Maille&)> >; _Tp1 = int; _Tp2 = int; _Proj = std::identity]’:
main.cpp:52:20:   required from here
   52 |     ranges::replace(tab | views::transform([](Maille& m){
      |     ~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   53 |         return m[HORIZONTAL];}),1,4);
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/c++/14/bits/iterator_concepts.h:561:13:   required for the satisfaction of ‘indirectly_writable<decltype (std::ranges::__access::__begin(declval<_Container&>())), const _Tp2&>’ [with _Range = std::ranges::transform_view<std::ranges::ref_view<Maille[2]>, main::._anon_117>; _Tp2 = int]
/usr/include/c++/14/bits/iterator_concepts.h:561:35:   in requirements with ‘_Out&& __o’, ‘_Tp&& __t’ [with _Out = std::ranges::transform_view<std::ranges::ref_view<Maille[2]>, main::._anon_117>::_Iterator<false>; _Tp = const int&]
/usr/include/c++/14/bits/iterator_concepts.h:563:14: note: the required expression ‘*__o =(forward<_Tp>)(__t)’ is invalid
  563 |         *__o = std::forward<_Tp>(__t);
      |         ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/c++/14/bits/iterator_concepts.h:564:34: note: the required expression ‘*(forward<_Out>)(__o)=(forward<_Tp>)(__t)’ is invalid
  564 |         *std::forward<_Out>(__o) = std::forward<_Tp>(__t);
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/c++/14/bits/iterator_concepts.h:566:11: note: the required expression ‘const_cast)())&&>(*__o) =(forward<_Tp>)(__t)’ is invalid
  565 |         const_cast<const iter_reference_t<_Out>&&>(*__o)
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  566 |           = std::forward<_Tp>(__t);
      |           ^~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/c++/14/bits/iterator_concepts.h:568:11: note: the required expression ‘const_cast)())&&>(*(forward<_Out>)(__o))=(forward<_Tp>)(__t)’ is invalid
  567 |         const_cast<const iter_reference_t<_Out>&&>(*std::forward<_Out>(__o))
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  568 |           = std::forward<_Tp>(__t);
      |           ^~~~~~~~~~~~~~~~~~~~~~~~
cc1plus: note: set ‘-fconcepts-diagnostics-depth=’ to at least 2 for more detail
main.cpp: In member function ‘int16_t& Maille::operator[](Direction)’:
main.cpp:25:9: warning: control reaches end of non-void function [-Wreturn-type]
   25 |         }
      |         ^

Solution

  • The reason you have the error is that lambdas return by value by default, so you are trying to assign to prvalues. You can fix this by specifying a return type for your lambda.

    [](Maille& m) -> int16_t& { return m[HORIZONTAL];}