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

5

35

I've been on this problem all morning with no result whatsoever. Basically, I need a simple metaprogramming thing that allows me to branch to different specializations if the parameter passed is a kind of std::vector or not.

Some kind of is_base_of for templates.

Does such a thing exist ?

Astatic answered 2/5, 2013 at 12:3 Comment(7)
With "specialization" do you mean inheritance? Or a type alias (e.g. typedef)? Or a specialized implementation for a certain type (like std::vector<bool> is)?Lancastrian
Your question is vague: if you want to determine if a type is a template specialization of std::vector for a type, you shouldn't be able to do it (not in a clean way anyway). If you want to determine if a type is inherited from std::vector<XXX>, this is explicitly advised against (std::vector doesn't have a virtual destructor and SHOULD NOT be inherited, only encapsulated). If you want to determine if a class/typedef/template parameter is a std::vector<XYZ, ...>, you should use a templated traits class (see answer from jrok).Foss
@utnapistim: It is not hard to check if a type is a specialization of a template in general.Palace
@DavidRodríguez-dribeas - I didn't mean check that a type is a std::vector<XYZ>, but that there is a template<> class vector<XYZ> particularized implementation (similar to how struct is_std_vector<std::vector<T,A>> is a particularized implementation of struct is_std_vector in jirok's answer). Is there a way of doing that?Foss
I asked a similar (but more general) question a while back: https://mcmap.net/q/428922/-how-can-i-check-if-a-type-is-an-instantiation-of-a-given-class-template-duplicate/20984Coshow
@utnapistim: I guess this is just a misunderstanding due to the overuse of the term 'specialization' in the standard meaning both when there is a separate definition for a set of template arguments and also the type/function generated after substitution of the template arguments in the base template. AFAIK you are right in that you cannot detect whether there is a different definition for a particular set of template arguments.Palace
possible duplicate of Doing a static_assert that a template type is another templateMcelrath
R
42

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;
}
Raina answered 1/3, 2015 at 17:9 Comment(5)
Hi! How could this example be changed to also support std::array which has a non-type template parameter?Suppositious
No, I don't think so. It's because there is no generic mechanism which is either a typename or a value-type and can hold a type or a concrete value. Sorry, I think you have to write a special is_std_array template.Raina
nice example. it would be very good to explain if for beginnersIncorruptible
For edification there's even a proposal to add this generic type trait, see proposal p2098r1Girovard
That's annoyingly trivial to implement, why isn't this in the standard library?Bledsoe
K
29

If you need a trait class it's pretty simple, you only need a general template and a specialization over any std::vector:

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

template<typename>
struct is_std_vector : std::false_type {};

template<typename T, typename A>
struct is_std_vector<std::vector<T,A>> : std::true_type {};

int main()
{
    typedef std::vector<int> vec;
    typedef int not_vec;
    std::cout << is_std_vector<vec>::value << is_std_vector<not_vec>::value;
}
Kirkcudbright answered 2/5, 2013 at 12:6 Comment(8)
To be complete, you should actually specialize for vector<T,A> to also catch vectors with custom allocators.Performing
Also note: implementations are allowed to add extra template arguments, so you might want to check. Although the above code will detect every instantiation of vector that uses the default values for the possible additional template parameters.Palace
If you want to catch all extensions, you could use template <typename T, typename... Ts> struct is_std_vector<std::vector<T, Ts...>> : std::true_type { }Annulus
@DavidRodríguez-dribeas That's a common misconception. The 'method of description' and the synopsis for e.g. std::vector are such that passing std::vector as a template template argument of the form template<typename, typename> class T is mandated to work -- meaning no extra parameter is allowed.Mealymouthed
@LucDanton: Can you point at where in the description that is stated. I have not checked this in C++11, but in C++03 implementations were allowed to add extra arguments as long as an object could be instantiated without passing those extra arguments by providing default values. But AFAIK the standard, does not guarantee that you can pass std::vector to a function taking a template<typename,typename> template argument.Palace
@DavidRodríguez-dribeas Good news! After some archaeology I found the original message by Daniel Krügler that pointed me to the right direction. In turn, the message links to Issue 94 of the Standard Library, taking us all the way back to 1998. (Stupidly I started looking in the language issues...)Mealymouthed
@LucDanton: How does that help? Lets say that in my implementation std::vector takes the type T, the allocator A and an enum E that controls some other behavior and is defaulted to e_default. Now consider the trait above and typedef std::vector<int,std::allocator<int>,e_not_default> myvector;. Up to here all is valid, right? What will be is_std_vector<myvector>::value? The trait will compile, but it will mark all vectors that have non-default values for the extra arguments as non-vector.Palace
@LucDanton: Rechecked with some committee members and they confirmed that the original intention was supporting extra template arguments, but that when template template arguments were actually implemented (at the language level) they found out that it couldn't be done. As it is now, types in the standard library are not allowed to take any extra argument.Palace
F
4

No, but you can overload with a template function which only accepts std::vector<T>. The compiler will choose the most specialized template in such cases.

Flour answered 2/5, 2013 at 12:5 Comment(0)
F
2

Defining the following two functions should work:

template<class T>
bool is_vector(const std::vector<T>& x) {return true;}

template<class T>
bool is_vector(const T& x) {return false;}

Example:

auto x {std::vector<double> {23, 32, 33}};
auto y {1.2};

std::cout << is_vector(x) << std::endl;
std::cout << is_vector(y) << std::endl;
Fretted answered 18/12, 2022 at 23:4 Comment(0)
I
1

I recently encountered almost the same question, which I need to figure out if something is a kind of another, for example, if a std::vector<std::vector<float>> is a kind of std::vector<WHATEVER> in my open cxx reflection lib. So I implement a generic is_fuzzy_type_match_v to check them, by using this consteval function, you can finish it in any kind of template, even variadic parameters template.

Here the code:

#include <type_traits>

namespace noaland {
    struct i_dont_care {};

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

    template<>
    struct is_i_dont_care<i_dont_care> : std::true_type {};

    // if two type are not the same
    template<typename X, typename Y>
    struct is_fuzzy_type_matched {
        consteval auto operator()() {
            if constexpr (noaland::is_i_dont_care<X>::value || noaland::is_i_dont_care<Y>::value || std::is_same_v<X, Y>) {
                return std::true_type{};
            } else {
                return std::false_type{};
            }
        }
    };

    template<typename X, typename Y>
    inline constexpr auto is_fuzzy_type_matched_v = decltype(is_fuzzy_type_matched<X, Y>()())::value;

    template<bool... R>
    consteval bool conjunction() {
        return (R && ...);
    }

    template<template<typename...> typename X, template<typename...> typename Y, typename... SUB_X, typename... SUB_Y>
    struct is_fuzzy_type_matched<X<SUB_X...>, Y<SUB_Y...>> {
        consteval auto operator()() {
            if constexpr (!conjunction<is_fuzzy_type_matched_v<SUB_X, SUB_Y>...>()) {
                return std::false_type{};
            } else {
                return std::true_type{};
            }
        }
    };
}

You can use this function like this:

int main() {

    static_assert(noaland::is_fuzzy_type_matched_v<std::vector<std::vector<std::vector<int>>>, std::vector<noaland::i_dont_care>>);

    return 0;
}

Since I use consteval which is a C++20 feature, you need to add --std=c++20, or you can just change consteval to constexpr.

If you are interested in the content presented in this article, please take a look at my work-in-progress cxx-reflection repository on GitHub. Your stars and forks are welcome.

Indies answered 21/4 at 3:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.