7

I want to check if two types are of the same template. As an example I want the following snippet of code to return true because both objects are vectors despite the inner elements being of different types.

It's important that the check is made at compile time (that's why the function is constexpr).

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

template <typename Container1, typename Container2> constexpr bool CheckTypes(Container1 c1, Container2 c2)
{
    return std::is_same<Container1,Container2>::value;
}

int main()
{
  std::vector<int> v1(100,0);
  std::vector<double> v2(100,0);
  std::cout << CheckTypes(v1,v2);
}
YSC
  • 38,212
  • 9
  • 96
  • 149
user28032019
  • 175
  • 8
  • If you need to check something at compile time, you should operate with rather static types than with variables. – vahancho Mar 28 '19 at 10:19
  • But they are not of the same type... The container is the same, not the instantiated type. You can check using `sizeof`, but I'm not sure how elegant of a solution that would be. – Constantinos Glynos Mar 28 '19 at 10:20
  • I want it to work with any container. – user28032019 Mar 28 '19 at 10:22
  • Why do you want to check this? – n. m. could be an AI Mar 28 '19 at 10:48
  • @Sombrero not quite happy with the dup. This Q is a bit different. OP gives `std::vector`as an example, but their questions is more general than that: _"I want to check if two types are of the same template"_. – YSC Mar 28 '19 at 10:55
  • @YSC I know the dup only asks the case for vector but the answer on that question answers it for every other specialization. That's why I hammered it. The question title is sub-optimal I agree. – Hatted Rooster Mar 28 '19 at 10:57

4 Answers4

5

Here you go:

template <class T, class U>
struct are_same_template : std::is_same<T, U>
{};

template <template<class...> class T, class T1, class T2>
struct are_same_template<T<T1>, T<T2>> : std::true_type
{};

template <class T, class U>
constexpr bool CheckTypes(T, U)
{
    return are_same_template<T, U>::value;
}

Demo: http://coliru.stacked-crooked.com/a/8533c694968f4dbb


This works by providing a specialization of are_same_template that discard the template argument types:

template <template<class...> class T, class T1, class T2>
struct are_same_template<T<T1>, T<T2>>

Even if T1 and T2 differ (the template argument types), are_same_template is a true type:

are_same_template<T<T1>, T<T2>> : std::true_type

About template<class...> instead of template<class>: this is to accomodate the fact than std:: containers have implicit template arguments. Thanks to ConstantinosGlynos for making me aware of it.

YSC
  • 38,212
  • 9
  • 96
  • 149
  • I like this approach, but why isn't it working here: https://rextester.com/LRJ63332 – Constantinos Glynos Mar 28 '19 at 10:31
  • I tried with g++, vc++ and clang.. All return the same result. The only difference is the c++17 flag. Godbolt also returns 0. https://godbolt.org/z/tVKxiv – Constantinos Glynos Mar 28 '19 at 10:36
  • I have a feeling that there's something horribly wrong with `Coliru`. Tried it with several compilers and platforms (`rextester, ideone, Qt` and `godbolt`). All give the same result (`false`) even with the `c++17` flag on. I think the `Coliru` compiler is acting up... – Constantinos Glynos Mar 28 '19 at 10:41
  • I don't think so... Because then it means `g++` and `vc++` have the same bug. Coliru is also using `g++`. Also, I was thinking, the `std::vector` container has a second template parameter for the `std::allocator`. The partial specialization does not include that parameter which makes me think that it's not the one being selected. Sweeet (i was right): https://rextester.com/MRXAQ82842 – Constantinos Glynos Mar 28 '19 at 10:47
  • @ConstantinosGlynos You're terribly right, and I'm wrong. This is an easy fix then :) Thank you. – YSC Mar 28 '19 at 10:49
  • And what happens if the class has two template parameters, one typename and one of a known type. For example `template class vector;` – user28032019 Mar 28 '19 at 10:52
  • @user28032019 try it for yourself ;) This answer manages simple types, simple templates, and now `std::` template containers. If you want something more exotic, please say so in the question. – YSC Mar 28 '19 at 10:53
  • @user28032019: Then you can specialize for that type. `std::array` takes two parameters for example (type and size). I still think this is a good idea. Partial class template specialization can be employed for all std containers. – Constantinos Glynos Mar 28 '19 at 10:55
  • Why the downvote now that the code has been fixed though? – YSC Mar 28 '19 at 10:56
  • @YSC: The fixed code works in all compilers and platforms! :-) – Constantinos Glynos Mar 28 '19 at 10:57
  • 1
    @YSC Your answer is fine and it's the accepted one. Can you help me now with a slighty different one https://stackoverflow.com/questions/55396786/check-if-class-is-a-template-specialization – user28032019 Mar 28 '19 at 11:46
2

In case you would like to check if two template template classes are the same without specializing them:

template<template<typename...> class ATT, template<typename...> class BTT>
struct is_same_tt : std::false_type {};

template<template<typename...> class TT>
struct is_same_tt<TT, TT> : std::true_type {};

// example
static_assert(is_same_tt<std::tuple, std::tuple>());
static_assert(!is_same_tt<std::vector, std::list>());
Sergey Kolesnik
  • 3,009
  • 1
  • 8
  • 28
1

You can but it take a little of metaprog :

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

template <typename Container1, typename Container2> 
struct CheckTypes_impl
{
    constexpr static bool check (Container1 , Container2 ) { return false; }
};

template <
    template <class...> class Container1, class...  args1 ,  class...  args2 > 
struct CheckTypes_impl<Container1<args1...>,Container1<args2...>>
{
    constexpr static bool check (Container1<args1...> , Container1<args2...> ) { return true; }
};


template < 
        template <class...> class Container1,
        class ... args1,
        template <class...> class Container2,
        class ... args2
    > constexpr bool CheckTypes(Container1<args1...> c1, Container2<args2...> c2)
{
    return CheckTypes_impl<Container1<args1...>,Container2<args2...>>::check(c1,c2);
}

int main()
{
  std::vector<int> v1(100,0);
  std::vector<double> v2(100,0);
  std::set<int> s;
  std::cout << CheckTypes(v1,v2)  << std::endl;
  std::cout << CheckTypes(v1,s)  << std::endl;
}

run : https://wandbox.org/permlink/OTuQfl7UBlbxgtCO

Update : You need " template class Container1, class..." because vector don't take 1 template param, but 2. In the general case you don't know how many default parameters will be used

Martin Morterol
  • 2,560
  • 1
  • 10
  • 15
0

Check this post. They provide a way to check if something is a specialization of a template class:

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 {};

And probably do something like this to keep your original interface:

template <typename Container1, typename Container2> 
constexpr bool CheckTypes(Container1 c1, Container2 c2) {
    return is_specialization<Container1, std::vector>::value && is_specialization<Container2, std::vector>::value;
}

So you can do something like this:

int main() {
    std::vector<int> v1(100,0);
    std::vector<double> v2(100,0);
    std::cout << CheckTypes(v1,v2);
    return 0;
}

mohabouje
  • 3,867
  • 2
  • 14
  • 28
  • 1
    And what happens if the class has two template parameters, one typename and one of a known type. For example `template class vector;` – user28032019 Mar 28 '19 at 10:52