6

I would like to check if two types are the same, but regardless of their template parameters. Something like this:

template<class T>
class A {};
class B {};

int main() {
    cout << std::is_same_template<A<int>, A<string>>::value << endl; // true
    cout << std::is_same_template<A<int>, B>::value << endl; // false
}

I am aware of std::is_same for checking if two types match exacty.

A reason why I need this: I have a templated method that can be called with any type, but I would like to prohibit that is is called with type A (which is templated), possibly by using a static_assert. Were A not templated, I believe it could be done easily using std::is_same, but now, I have a problem...

EDIT: I can manually exclude A for a few common Ts, using, I am looking for a way to do it for all T:

static_assert(!std::is_same<parameter_type, A<int>>::value, "Cannot use this function with type A<T>");
static_assert(!std::is_same<parameter_type, A<double>>::value, "Cannot use this function with type A<T>");
static_assert(!std::is_same<parameter_type, A<bool>>::value, "Cannot use this function with type A<T>");
jureslak
  • 195
  • 2
  • 9
  • Two types are not the same, if their template parameters are different. Thus, `A` and `A` are far from being the same type. – skypjack Oct 24 '15 at 21:47
  • 3
    Well, I am well aware of that, that is why I can't use `std::is_same`. What I am asking is, if there is way to compare less strictly, to make A and A compare equal and A and B not? – jureslak Oct 24 '15 at 21:49
  • You can use the reversed approach by means of template specialization, thus accept only the allowed classes and define an empty/broken/throwing/whatever function otherwise. – skypjack Oct 24 '15 at 21:51
  • I thought of that myself as well, but it is not really optimal, since I would like my function to be used for any type, but this one. It would require me to write a lot of specializations :) As I can disallow a fixed type, I would rather disallow A for some common Ts. – jureslak Oct 24 '15 at 21:57
  • Can't you refactor `A` in order to have there a templated component/delegate, so that you'll manage to use `is_same` with `A`? – skypjack Oct 24 '15 at 22:03
  • Do not judge classes by their name but rather by their their deads (if you know what I mean) – Lol4t0 Oct 24 '15 at 22:09
  • @skypjack Uf, that would be unnatural, I believe, this detail is not important enogh. The question is also partially of theoretical interest :) – jureslak Oct 24 '15 at 22:18
  • @Lol4t0 That is a very valid point. I can call the function with any "container" but I wanted to prevent calling that function with A (which is a container, otherwise the function wouldnt even compile, and there would be no trouble) because the result is somewhat weird (eventhough logical), and I wanted to prevent future bugs that it might cause. – jureslak Oct 24 '15 at 22:18
  • Still may be you would be able to specialize some requirement for class interface. Even knowing that your class is container gives you check for `BadContainer is same to ContainerInQuestion` – Lol4t0 Oct 24 '15 at 22:28
  • Hmm, I don't quite understand what you mean, but it might lead to the same solution as proposed below (although that one is more direct and not so "specify the interface" like). My problem is that I am extracting a subcontainer from a container, and some containers (type `A` in the question) are of fixed length, so extracting a subset of `k` elements with current code returns container of equal length, but only first `k` elements filled. Inside the function a container `parameter_type ret(k)` is created, which is a valid constructor call for the fixed sized ones, but does nothing... – jureslak Oct 24 '15 at 22:39
  • I would just check `assert(result.size() == k)`. That is not compile time but is clear. – Lol4t0 Oct 24 '15 at 23:05

2 Answers2

7

I've come up with a simpler way than @NikitaKakuev's answer, currently used on a project of mine.

template<typename, typename>
constexpr bool is_same_template{false};

template<
    template<typename...> class T, //typename T in C++17
    typename... A,
    typename... B
>
constexpr bool is_same_template<
    T<A...>,
    T<B...>
>{true};

The only current issue is with templates that mix types and typenames, ie. std::array<int, 10>.
To overcome this limitation, specialization is required.

Usage:

bool b = is_same_template<std::string, std::wstring>;  //true
//both are typedefs of std::basic_string

EDIT: As requested by @Helix in the comment below, specialization for std::array (or any other template with the same signature):

template<
    template<typename, std::size_t> class T, //typename T in C++17
    typename TA, std::size_t NA,
    typename TB, std::size_t NB
>
constexpr bool is_same_template<
    T<TA, NA>,
    T<TB, NB>
>{true};
bit2shift
  • 656
  • 1
  • 9
  • 17
5

Okay, how about this:

#include <iostream>

template<class T>
struct A {};
struct B {};

template <typename T>
struct should_reject { static constexpr bool value = false; };

template <typename T>
struct should_reject<A<T>> { static constexpr bool value = true; };

template <typename T>
void rejectA(T t)
{
    std::cout << should_reject<T>::value << std::endl;
}

int main() {
    rejectA(B());         // false
    rejectA(1);           // false
    rejectA(1.0);         // false
    rejectA(A<B>());      // true
    rejectA(A<int>());    // true
    rejectA(A<double>()); // true
}
Nikita Kakuev
  • 1,096
  • 9
  • 13
  • I would like those two in you example to compare equal, but you gave me some ideas. Could I tweak to function to reject the types, which have a cetain typdef defined? Or perhaps even a runtime value... (and the downvote is not mine) – jureslak Oct 24 '15 at 22:01
  • Oh, I totally misread your question. Sorry for that. – Nikita Kakuev Oct 24 '15 at 22:04
  • I've edited my answer. – Nikita Kakuev Oct 24 '15 at 22:10
  • Still not quite what I wanted, but I think I can actually make a solution from here on. A should_reject should have true for all A, but false by default. – jureslak Oct 24 '15 at 22:16
  • Check the updated version. – Nikita Kakuev Oct 24 '15 at 22:43
  • Yep, exactly the same as the one I came up with (after your previous comment). This is a solution to my (somewhat theoretical) question :) – jureslak Oct 24 '15 at 22:45
  • Well done. You can even return an error at compile time by using `enable_if` within `rejectA` template declaration, can't you? – skypjack Oct 24 '15 at 22:55
  • You can indeed, but you'll end up with something like `template ::value>::type>` which is not very readable. This will also lead to puzzling error messages during compilation. I think it will be better to use a `static_assert` instead of `enable_if`. – Nikita Kakuev Oct 24 '15 at 23:18