33

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 ?

phuclv
  • 37,963
  • 15
  • 156
  • 475
Michael
  • 1,357
  • 3
  • 15
  • 24
  • With "specialization" do you mean inheritance? Or a type alias (e.g. `typedef`)? Or a specialized implementation for a certain type (like `std::vector` is)? – Some programmer dude May 02 '13 at 12:06
  • 1
    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, 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, you should use a templated traits class (see answer from jrok). – utnapistim May 02 '13 at 12:16
  • @utnapistim: It is not *hard* to check if a type is a specialization of a template in general. – David Rodríguez - dribeas May 02 '13 at 12:52
  • @DavidRodríguez-dribeas - I didn't mean check that a type is a std::vector, but that there is a template<> class vector particularized implementation (similar to how `struct is_std_vector>` is a particularized implementation of `struct is_std_vector` in jirok's answer). Is there a way of doing that? – utnapistim May 02 '13 at 13:06
  • I asked a similar (but more general) question a while back: http://stackoverflow.com/q/11251376/20984 – Luc Touraille May 02 '13 at 13:18
  • 2
    @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. – David Rodríguez - dribeas May 02 '13 at 13:59
  • possible duplicate of [Doing a static\_assert that a template type is another template](http://stackoverflow.com/questions/17390605/doing-a-static-assert-that-a-template-type-is-another-template) – Quuxplusone Aug 20 '13 at 02:04

4 Answers4

38

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;
}
Rishabh Deep Singh
  • 807
  • 1
  • 12
  • 24
Databyte
  • 1,420
  • 1
  • 15
  • 24
  • Hi! How could this example be changed to also support std::array which has a non-type template parameter? – Pascalau Razvan Aug 24 '15 at 16:14
  • 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. – Databyte Aug 26 '15 at 21:05
  • nice example. it would be very good to explain if for beginners – Soheil Armin Nov 07 '19 at 19:40
  • 2
    For edification there's even a proposal to add this generic type trait, see [proposal p2098r1](http://open-std.org/JTC1/SC22/WG21/docs/papers/2020/p2098r1.pdf) – klaus triendl Aug 22 '20 at 18:54
  • 1
    That's annoyingly trivial to implement, why isn't this in the standard library? – Not Saying Oct 07 '21 at 22:45
26

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;
}
keineahnung2345
  • 2,635
  • 4
  • 13
  • 28
jrok
  • 54,456
  • 9
  • 109
  • 141
  • 5
    To be complete, you should actually specialize for `vector` to also catch vectors with custom allocators. – dhavenith May 02 '13 at 12:09
  • 1
    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. – David Rodríguez - dribeas May 02 '13 at 12:53
  • 2
    If you want to catch all extensions, you could use `template struct is_std_vector> : std::true_type { }` – MadScientist May 02 '13 at 13:12
  • 1
    @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 class T` is mandated to work -- meaning no extra parameter is allowed. – Luc Danton May 07 '13 at 04:50
  • @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` template argument. – David Rodríguez - dribeas May 07 '13 at 05:08
  • 2
    @DavidRodríguez-dribeas Good news! After some archaeology I found [the original message by Daniel Krügler](https://groups.google.com/d/msg/comp.lang.c++.moderated/bHtHTmQ0DIg/SQvPSnfHuywJ) that pointed me to the right direction. In turn, the message links to [Issue 94 of the Standard Library](http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-closed.html#94), taking us all the way back to 1998. (Stupidly I started looking in the *language* issues...) – Luc Danton May 07 '13 at 05:41
  • @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,e_not_default> myvector;`. Up to here all is valid, right? What will be `is_std_vector::value`? The trait will compile, but it will mark all vectors that have non-default values for the extra arguments as non-vector. – David Rodríguez - dribeas May 07 '13 at 13:15
  • 4
    @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. – David Rodríguez - dribeas May 07 '13 at 15:40
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.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
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;