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
?
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) {}
};