How to use std::iota with std::pair?

603 views Asked by At

Suppose I have to use this template which I cannot modify:

// cannot modify
template <typename C,typename T> 
void foo(C& c,const T& t) {
    // ...
    std::iota(std::begin(c),std::end(c),t);
    // ...
}

It fails when c is a container of std::pairs:

#include <vector>
#include <utility>
#include <numeric>
int main()
{
    std::vector<std::pair<int,int>> y(5);
    foo(y,std::pair<int,int>{1,2});
}

Expected contents of y is {1,2},{2,3},{3,4},{4,5},{5,6}.

The error is:

In file included from /opt/compiler-explorer/gcc-9.2.0/include/c++/9.2.0/numeric:62,
                 from <source>:2:
/opt/compiler-explorer/gcc-9.2.0/include/c++/9.2.0/bits/stl_numeric.h: In instantiation of 'void std::iota(_ForwardIterator, _ForwardIterator, _Tp) [with _ForwardIterator = __gnu_cxx::__normal_iterator<std::pair<int, int>*, std::vector<std::pair<int, int> > >; _Tp = std::pair<int, int>]':
<source>:9:14:   required from 'void foo(C&, const T&) [with C = std::vector<std::pair<int, int> >; T = std::pair<int, int>]'
<source>:16:34:   required from here
/opt/compiler-explorer/gcc-9.2.0/include/c++/9.2.0/bits/stl_numeric.h:94:4: error: no match for 'operator++' (operand type is 'std::pair<int, int>')
   94 |    ++__value;
      |    ^~~~~~~~~
Compiler returned: 1

Ok, I understand that std::pair has no operator++, but if I provide one myself I cannot restrict its scope to foo.

How can I use std::iota with containers of std::pair<T,U> when I am not allowed to define a template <typename T,typename U> std::pair<T,U>::operator++ ?

1

There are 1 answers

0
Scheff's Cat On BEST ANSWER

Please, note that std::iota() has separate types for the iterator and the value to increment/assign:

template< class ForwardIt, class T >
void iota( ForwardIt first, ForwardIt last, T value );

This can be utilized to use a type for value which is different from (though assignable to) the element type of vector.

In my case, I simply derived a type from the std::pair (to be used in the std::vector) and added the increment operator to that derived type:

#include <numeric>
#include <iostream>
#include <vector>

template <typename T1, typename T2>
struct PairIncT: std::pair<T1, T2> {
    PairIncT(T1 first, T2 second): std::pair<T1, T2>(first, second) { }
    PairIncT& operator++() { ++this->first; ++this->second; return *this; }
};

int main()
{
  // a vector of pairs
  using VectorIU = std::vector<std::pair<int, unsigned>>;
  VectorIU v(5);
  // make corresponding inc. type for vector element
  using PairIU = PairIncT<
    VectorIU::value_type::first_type,
    VectorIU::value_type::second_type
  >;
  // fill vector
  std::iota(v.begin(), v.end(), PairIU( 1, 2 ));
  // show vector
  for (const VectorIU::value_type &elem : v) {
    std::cout << "{ " << elem.first << ", " << elem.second << " }\n";
  }
}

Output:

{ 1, 2 }
{ 2, 3 }
{ 3, 4 }
{ 4, 5 }
{ 5, 6 }

Live Demo on coliru