Reputation: 1801
I want to determine if something is a container. Right now I'm testing it on containers that are formatted to have a type and allocator. My ultimate goal is to learn how to write a container template that, when containing layers of itself can allow direct iteration on its innermost elements. E.g. if there was a container of 3 containers each containing 3 more containers, each containing 3 elements, I want to be able to iterate through all 27 elements in one range based for loop. Distinguishing whether something is a container that directly contains elements or contains containers of elements (or containers of containers...of containers of elements) using SFINAE to check whether the element has an iterator seems like a logical first step towards determining whether the begin()
function should return the iterator to the element or the result of said element's begin()
function. If I can get my first step to work, I'd like to include that logic into writing my container, but I can't get it to compile:
#include <vector>
#include <iostream>
template <typename T,
template <typename E, typename Allocator = std::allocator<E>> class Container
>
void is_cont(typename Container<T>::iterator it)
{
std::cout << "is iterator\n";
}
template <typename T,
template <typename E, typename Allocator = std::allocator<E>> class Container
>
void is_cont(Container<T>& cont)
{
std::cout << "is container\n";
}
int main()
{
std::vector<int> vec{ 2, 4, 6 };
is_cont(vec); // Output: "is container"
//is_cont(vec.begin()); // COMPILER ERROR
}
How can I fix this?
Upvotes: 3
Views: 798
Reputation: 118352
Your definition of whether something is or is not a container appears to be based solely on whether it has an iterator
inner class.
That's as good of a heuristic as any, I suppose, but let's go with that.
It most instances, SFINAE
is much simpler if it's done with classes, instead of trying to implement SFINAE
with a function, like your is_container()
. Making your is_container()
into a class results in a following, textbook SFINAE
solution:
#include <vector>
#include <string>
#include <iostream>
#include <type_traits>
template<typename ...>
using void_t=void;
template<typename T, class=void> struct is_container : std::false_type {};
template<typename T>
struct is_container<T, void_t<typename T::iterator>> : std::true_type {};
int main()
{
std::cout << is_container<int>::value << std::endl;
std::cout << is_container<std::vector<int>>::value << std::endl;
return 0;
}
Now, a related question would be whether there's a better way to check if something is a container, or not. Whichever heuristic you choose, it's trivial to adjust a class-based test, instead of a function-based. For example, if you want to consider whether something is a container if it has both iterator
and const_iterator
inner classes, just need to change one line:
template<typename T>
struct is_container<T, void_t<typename T::iterator,
typename T::const_iterator>> : std::true_type {};
Upvotes: 6