sfinae to detect containers: failure for std:array
Asked Answered
C

2

6

I am looking for a way to use SFINAE to implement some function, that must be available only to some containers: vector, list, array (set is there below only as a test)

Build upon this answer, I tried the code below that uses a traits class that returns true only for the required containers.

As you can see online here, it fails for std::array.

template <typename Container>
struct is_container : std::false_type { };

template <typename... Ts> struct is_container<std::array<Ts... >> : std::true_type { };
template <typename... Ts> struct is_container<std::vector<Ts...>> : std::true_type { };
template <typename... Ts> struct is_container<std::set<Ts...   >> : std::true_type { };
template <typename... Ts> struct is_container<std::list<Ts...  >> : std::true_type { };
template <typename... Ts> struct Dummy{};

int main()
{
    std::cout << "Dummy: " << is_container<Dummy<int>>::value << '\n';
    std::cout << "array: " << is_container<std::array<int,5>>::value << '\n';
    std::cout << "vector:" << is_container<std::vector<int>>::value << '\n';
    std::cout << "set: "   << is_container<std::set<int>>::value << '\n';
    std::cout << "list: "  << is_container<std::list<int>>::value << '\n';
}

What I understand is that this is due to the fact that std::array requires a second template parameter. I have a low experience with variadic templates, so my question is:

Is there a way to make this approach successful? Or shall I use another approach described in the linked question?

I'd rather have something pure C++11, but C++14 would be ok too.

Consols answered 23/6, 2021 at 9:30 Comment(2)
The problem is that second template parameter is not a type but a number. Thus the specialisation doesn't fit.Adynamia
i was about to add the c++11 tag but it was interleaved with another edit that added already the max 5 tags. Imho it would be nice to have the c++11 tag but didnt want to decide what else to removeFinnell
R
7

This is not SFINAE but regular template specialisation. Your std::array is not recognised because a value of type std::size_t (which ist std::array's second argument) is not a typename.

You can change your check for array specifically:

template <typename T, std::size_t N> struct is_container<std::array<T,N>> : std::true_type { };
template <typename... Ts> struct is_container<std::vector<Ts...>> : std::true_type { };

If you actually want to use SFINAE to check for anything that behaves like a container, you could check for the existance of std::begin, std::end, std::size for that type.

Ramble answered 23/6, 2021 at 9:34 Comment(7)
Sorry if it wasn't clear, I meant this will be used in some Sfinae template function.Consols
@Consols I see. Well you could actually allow for a more generic test if you check for properties of a container than listing the container explicitly. What if your user decides to use a std::unordered_set rather than a std::set or a std::deque instead of a std::list. Or a custom container, or ...Ramble
The point is to allow the function code only the 3 listed constainers. For std::set, the idea is to let the build fail.Consols
Ah. In that case you may want to change the set line to template <typename... Ts> struct is_container<std::set<Ts... >> : std::false_type { }; or just completely remove that line because you have a default that goes to false_type.Ramble
Sure, yes. It was there just as a test. Or better, I'll just remove that line.Consols
Does this answer you question, then?Ramble
Yep, the two answers are basically the same, usually when that happen, I accept the one from the lowest rep ;-) Thanks.Consols
I
5

The problem is that the 2nd template parameter of std::array is a non-type template parameter, which doesn't match type template parameter pack typename... Ts.

You can change the specialization for std::array to:

template <typename T, std::size_t S> struct is_container<std::array<T, S>> : std::true_type { };
Ichnology answered 23/6, 2021 at 9:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.