Reputation: 3705
I was wondering if there is a way to create a template function which takes a reference to ANY collection of a specific type. i.e. something like:
class Bob
{
public:
int age;
int height;
}
template<class T>
void functionWhichIteratesOverBobs(T &bobs)
{
int totalAge = 0;
for(auto &bob: bobs)
{
totalAge += bob.age;
}
}
Basically is there a way to, in the definition of the template function, require that T have a begin() and end() function which return an iterator to T.
I've seen the following question, but that would require a function which takes a begin and end, i.e.
std::vector<Bob> bobs;
functionWhichIteratesOverBobs(bob.begin(), bob.end());
when what I would like is:
std::vector<Bob> bobs;
functionWhichIteratesOverBobs(bobs);
Function that takes an STL iterator over ANY container of a elements of a specific type
Upvotes: 4
Views: 1160
Reputation: 1788
With C++ 20 you can use the range concept:
#include <ranges>
template<std::ranges::range T>
void functionWhichIteratesOverBobs(T &bobs)
This will check that T is a range, i.e. has begin and end.
There are more fine grained concepts in the range library.
https://en.cppreference.com/w/cpp/ranges#Range_concepts
I find this much more readable than SFINAE
Upvotes: 0
Reputation: 70038
Yes. It's possible.
As Ed S. comment, just use the same function which you wrote in your question. If the bobs
doesn't have begin()
and end()
then compiler will notify you by generating error.
Demo.
Upvotes: 0
Reputation: 157424
The C++ concept Container has a member type value_type
the type of the contained elements, so you can use SFINAE just as you would with an iterator - it's even simpler, because you don't need to use iterator_traits
:
template<class T>
auto functionWhichIteratesOverBobs(T &bobs)
-> std::enable_if_t<std::is_same_v<typename T::value_type, Bob>>
{
// ...
}
Note that this won't work with raw arrays; if you want those to work you would use decltype(std::begin(bobs))
and iterator_traits
:
template<class T>
auto functionWhichIteratesOverBobs(T &bobs) -> std::enable_if_t<std::is_same_v<
typename std::iterator_traits<decltype(std::begin(bobs))>::value_type, Bob>>
{
// ...
}
Upvotes: 0
Reputation: 60999
If you're looking to keep the overload set unpolluted, use expression SFINAE like so:
template<class T>
void functionWhichIteratesOverBobs(T &bobs)
-> decltype(std::begin(bobs), std::end(bobs), void()) {
// [..Range based for over bobs..]
}
Keep in mind though that the function template shown by you won't instantiate without an error if given a wrong argument, so this is currently superfluous - until you start overloading functionWhichIteratesOverBobs
.
Upvotes: 1