What I would do is create a test that checks whether you can call std::begin
and std::end
on your container. And then use iterators to those containers and std::advance
to advance your iterators to where you need them.
The advantage of this method is then you can accept containers that don't overload operator[]
(like std::list
for example)
#include <utility>
#include <type_traits>
#include <vector>
#include <list>
namespace sfinae_true_details
{
template <class>
struct sfinae_true : std::true_type{};
}
template <class T>
auto test_has_begin_end(int) ->
sfinae_true_details::sfinae_true<decltype(std::begin(std::declval<T>()) ==
std::end(std::declval<T>())>;
template <class>
std::false_type test_has_begin_end(long);
template <class T>
struct has_begin_end : decltype(test_has_begin_end<T>(0)){};
int main()
{
static_assert(has_begin_end<std::vector<int> >::value, "");
static_assert(has_begin_end<std::list<float> >::value, "");
static_assert(has_begin_end<int>::value, "Expected to fail here");
return 0;
}
Then your usage would be:
template <typename T, typename C>
static T GetItemFromContainer(const C &container) {
static_assert(has_begin_end<C>::value, "Must pass a container for which "
"std::begin and std::end are callable");
T item = *std::begin(container); // do something. 0 or iterator
auto iter = std::begin(container);
std::advance(iter, 5);
item = *iter; // equivalent to container[5] for RandomAccessContainers
return item;
}