c++iterator

C++ function parameter that only accepts iterator to a specific type


I know I can use a template to make a constructor which accepts any type of iterator like this:

struct Thing
{
  std::vector<int> integers;
  std::list<std::string> strings;

  template <typename InputIt>
  Thing(InputIt start, InputIt end): integers(start, end) {}
};

Or, I can make constructors that accept the specific iterator type of the containers I'm using:

struct Thing
{
  std::vector<int> integers;
  std::list<std::string> strings;

  Thing(std::vector<int>::iterator start, std::vector<int>::iterator end): integers(start, end) {}
  Thing(std::list<std::string>::iterator start, std::list<std::string>::iterator end): strings(start, end) {}
};

I would like to do the above, but I don't want the iterators to be restricted to a specific type of container.

I imagine having some types IntInputIt and StringInputIt that can only refer to int or std::string iterators, like this:

struct Thing
{
  std::vector<int> integers;
  std::list<std::string> strings;

  Thing(IntInputIt start, IntInputIt end): integers(start, end) {}
  Thing(StringInputIt start, StringInputIt end): strings(start, end) {}
};

Then I could initialize my struct from any kind of list:

std::vector<int> ints1({1,2,3});
std::list<int> ints2({4,5,6});
auto thing1 = Thing(ints1.begin(), ints1.end());
auto thing2 = Thing(ints2.begin(), ints2.end());

Is there a straightforward way to accomplish my imagined IntInputIt and StringInputIt?


Solution

  • With C++20 concepts you can easily do this with:

    #include <iterator>
    
    class Thing
    {
      std::vector<int> integers;
      std::list<std::string> strings;
    
    public:
      template<std::input_iterator InputIt>
        requires std::same_as<std::iter_value_t<InputIt>, int>
      Thing(InputIt start, InputIt end): integers(start, end) {}
    
      template<std::input_iterator StringInputIt>
        requires std::same_as<std::iter_value_t<StringInputIt>, std::string>
      Thing(StringInputIt start, StringInputIt end): strings(start, end) {}
    };