Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

sfinae to detect containers: failure for std:array

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.

like image 925
kebs Avatar asked Jun 23 '21 09:06

kebs


People also ask

What is Sfinae used for?

Substitution failure is not an error (SFINAE) refers to a situation in C++ where an invalid substitution of template parameters is not in itself an error. David Vandevoorde first introduced the acronym SFINAE to describe related programming techniques.

What does template typename t mean?

template <typename T> ... This means exactly the same thing as the previous instance. The typename and class keywords can be used interchangeably to state that a template parameter is a type variable (as opposed to a non-type template parameter).

What are SFINAE errors?

Only the failures in the types and expressions in the immediate context of the function type or its template parameter types or its explicit specifier (since C++20) are SFINAE errors.

What is SFINAE in C++?

SFINAE in partial specializations Deduction and substitution also occur while determining whether a specialization of a class or variable (since C++14) template is generated by some partial specialization or the primary template.

Should I use SFINAE or static_assert in C++?

Where applicable, tag dispatch, if constexpr (since C++17), and concepts (since C++20) are usually preferred over use of SFINAE. static_assert is usually preferred over SFINAE if only a conditional compile time error is wanted.

What metafunction must be implemented with SFINAE?

In addition, many type traits must be implemented with SFINAE if appropriate compiler extensions are unavailable. The standard library component std::void_t is another utility metafunction that simplifies partial specialization SFINAE applications.


2 Answers

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.

like image 63
bitmask Avatar answered Oct 08 '22 01:10

bitmask


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 { };
like image 29
songyuanyao Avatar answered Oct 08 '22 01:10

songyuanyao