How to Know If a Type Is a Specialization of Std::Vector

How to know if a type is a specialization of std::vector?

In C++11 you can also do it in a more generic way:

#include <type_traits>
#include <iostream>
#include <vector>
#include <list>

template<typename Test, template<typename...> class Ref>
struct is_specialization : std::false_type {};

template<template<typename...> class Ref, typename... Args>
struct is_specialization<Ref<Args...>, Ref>: std::true_type {};

int main()
{
typedef std::vector<int> vec;
typedef int not_vec;
std::cout << is_specialization<vec, std::vector>::value << is_specialization<not_vec, std::vector>::value;

typedef std::list<int> lst;
typedef int not_lst;
std::cout << is_specialization<lst, std::list>::value << is_specialization<not_lst, std::list>::value;
}

How can I check if template type is any type of std::vector

You can use partial specialization:

#include <type_traits>
#include <iostream>
#include <vector>

template <typename C> struct is_vector : std::false_type {};
template <typename T,typename A> struct is_vector< std::vector<T,A> > : std::true_type {};
template <typename C> inline constexpr bool is_vector_v = is_vector<C>::value;

int main() {
std::cout << is_vector_v< std::vector<int> > << "\n";
std::cout << is_vector_v< int > << "\n";
}

Detect whether a type is a vector of enum

Specializations are allowed to have a different number of template parameters than the primary. In fact, this happens quite often. However, as the error indicates, you are not allowed to give any of them default arguments.

That aside, I prefer simplicity, when possible.

template <typename T>
struct is_vector_enum : std::false_type { };

template <typename T>
struct is_vector_enum<std::vector<T>> : std::is_enum<T> { };

How to check if there is a specialization for std::less, using concepts?

Any type that is less-than comparable is comparable with std::less. That is, the primary std::less template will invoke operator< for any T it is given. You can provide an explicit specialization for some user-defined type, but you don't have to if your type is operator< comparable.

std::complex is not less-than comparable by nature. However, you're not allowed to provide specializations for a standard library template if the all of the template parameters for the specialization are themselves standard-library types. Trying to do so achieves UB, so you could get any result.

But in any case, std::hash is something you must provide a specialization for. That's part of what it means to make a type "hashable".

These are two different designs for different kinds of functionality. A type can implicitly be less-than comparable, but a type cannot implicitly be hashable.

And no, there is no way to detect with a concept whether a particular template instantiation comes from a primary template or a specialization.

If what you're trying to do is see if a type is less-than comparable, you shouldn't be relying on std::less at all. Instead, you should be using the std::totally_ordered concept.

Check if class is a template specialization

C++20 is a weird, weird world. Cross-checking is welcome as I'm a beginner with CTAD and not entirely sure I've covered all bases.

This solution uses SFINAE to check whether class template argument deduction (CTAD) succeeds between the requested class template and the mystery type. An additional is_same check is performed to prevent against unwanted conversions.

template <auto f>
struct is_specialization_of {
private:
template <class T>
static auto value_impl(int) -> std::is_same<T, decltype(f.template operator()<T>())>;

template <class T>
static auto value_impl(...) -> std::false_type;

public:
template <class T>
static constexpr bool value = decltype(value_impl<T>(0))::value;
};

// To replace std::declval which yields T&&
template <class T>
T declrval();

#define is_specialization_of(...) \
is_specialization_of<[]<class T>() -> decltype(__VA_ARGS__(declrval<T>())) { }>::value

// Usage
static_assert(is_specialization_of(std::array)<std::array<int, 4>>);

First caveat: Since we can't declare a parameter for the class template in any way without knowing its arguments, passing it around to where CTAD will be performed can only be done by jumping through some hoops. C++20 constexpr and template-friendly lambdas help a lot here, but the syntax is a mouthful, hence the helper macro.

Second caveat: this only works with movable types, as CTAD only works on object declarations, not reference declarations. Maybe a future proposal will allow things such as std::array &arr = t;, and then this will be fixed!

Actually fixed by remembering that C++17 has guaranteed copy-elision, which allows direct-initialization from a non-movable rvalue as is the case here!

Check if class is a template specialization?

Here's one where you can provide the template to be matched against :

template <class T, template <class...> class Template>
struct is_specialization : std::false_type {};

template <template <class...> class Template, class... Args>
struct is_specialization<Template<Args...>, Template> : std::true_type {};

static_assert(is_specialization<std::vector<int>, std::vector>{}, "");
static_assert(!is_specialization<std::vector<int>, std::list>{}, "");

Partial specialization for std::vector of all types

You're almost there, all you need is the syntax.

// SPECIAL CASE with vector of vectors
template < typename element_type >
class Container<std::vector< element_type >>

You could also extract the allocator type from vector, if you actually want to support different allocators.

I'm not sure whether I'm going too far with templateization

If you have a choice between writing a template or not, usually you shouldn't. Otherwise, nothing here is suspicious.

Specialization of class for vectors of specific type_trait

The problem is, that std::hash has only a single template parameter and you cannot add an additional defaulted template parameter in a partial specialization. So you have several choices, depending on what you want to do with your hash.

Please also rethink your approach. This comment by Yakk is very useful:

You may not specialize templates in std unless the specialization depends on a user-provided type. – Yakk

You can easily fix thisby not putting your own hash into the std namespace.

  1. You could simply remove the enable_if from the template argument list and replace it with a

    static_assert(std::is_arithmetic<dtype>::value, "!");

    in the struct body, given that you only ever want to hash vectors of arithmetic type.

  2. SFINAE on the call operator. This way, you have to provide all the hash methods for all other vector types within the same struct. Also you have to go through some funny business of repeating the template parameter to make the compiler happy. It's very important that your SFINAE criteria are mutually exclusive, otherwise you'll get horrible errors.

    #include <iostream>
    #include <string>
    #include <type_traits>
    #include <vector>

    namespace std {

    template< typename dtype >
    struct hash< std::vector<dtype> >
    {
    template< typename T = dtype >
    std::enable_if_t<std::is_arithmetic<T>::value, size_t>
    operator()(const std::vector<T> &input) const
    {
    constexpr size_t FNV_prime = 1099511628211ul;
    constexpr size_t FNV_offset = 14695981039346656037ul;

    size_t hashed = FNV_offset;
    for(const auto &n:input)
    {
    hashed ^= n;
    hashed *= FNV_prime;
    }
    return hashed;
    }

    template< typename T = dtype >
    std::enable_if_t<!std::is_arithmetic<T>::value, size_t>
    operator()(const std::vector<T> &input) const
    {
    std::cout << "No hash for you :-(\n";
    return 0;
    }
    };

    } // namespace std

    int main() {
    {
    std::vector<int> v{1,2,3,4};
    size_t hash = std::hash<std::vector<int>>{}(v);
    std::cout << hash << "\n";
    }

    {
    std::vector<std::string> v{"Hello", "world!"};
    size_t hash = std::hash<std::vector<std::string>>{}(v);
    std::cout << hash << "\n";
    }
    }

    Live example

  3. You can also declare your own struct for hashing and add as many template parameters as you want. Then you just need std::hash to inherit from your custom struct.

    #include <iostream>
    #include <string>
    #include <type_traits>
    #include <vector>

    template < typename T, bool = std::is_arithmetic<T>::value >
    struct vector_hash;

    template < typename T>
    struct vector_hash<T,true> {
    size_t operator()(std::vector<T> const &input) const
    {
    constexpr size_t FNV_prime = 1099511628211ul;
    constexpr size_t FNV_offset = 14695981039346656037ul;

    size_t hashed = FNV_offset;
    for(const auto &n:input)
    {
    hashed ^= n;
    hashed *= FNV_prime;
    }
    return hashed;
    }
    };

    template < typename T>
    struct vector_hash<T,false> {
    size_t operator()(std::vector<T> const &) const
    {
    std::cout << "No hash for you :-(\n";
    return 0;
    }
    };

    namespace std {

    template< typename dtype >
    struct hash< std::vector<dtype> > : vector_hash<dtype> {};

    } // namespace std

    int main() {
    {
    std::vector<int> v{1,2,3,4};
    size_t hash = std::hash<std::vector<int>>{}(v);
    std::cout << hash << "\n";
    }

    {
    std::vector<std::string> v{"Hello", "world!"};
    size_t hash = std::hash<std::vector<std::string>>{}(v);
    std::cout << hash << "\n";
    }
    }

    Live example

Specialize template function to return vector

You can't partially specialize functions. You can overload them though, but the way of doing it is not obvious, since your function doesn't take any parameters.

First, you need a way to check if a type is a std::vector<??>:

template <typename T> struct IsVector : std::false_type {};
template <typename ...P> struct IsVector<std::vector<P...>> : std::true_type {};

Then you can plug it into requires:

template <typename T>
T Read()
{
// Generic overload
}

template <typename T> requires IsVector<T>::value
T Read()
{
// Vector overload
}

Alternatively, you could have a single function, with if constexpr (IsVector<T>::value) inside.

Template class specialization for vector type - different valid syntax?

Given the definition of the class template,

template<> // <--- optional?
template<typename T>
struct PrintMe<std::vector<T>> { ... };

is not valid.

You need to remove that line and use:

template<typename T>
struct PrintMe<std::vector<T>> { ... };


Related Topics



Leave a reply



Submit