17

How do I static_assert like this? Maybe Boost supports it if not C++ or new features in C++11?

template<T>
struct foo {};

template<FooType>
struct bar {
  static_assert(FooType is indeed foo<T> for some T,"failure"); //how?
};
roger.james
  • 1,478
  • 1
  • 11
  • 23
  • A related question can be found [here](http://stackoverflow.com/questions/12919310/c-detect-templated-class/12919396) – Tom Knapen Jun 30 '13 at 13:19

3 Answers3

31

You could do something along these lines. Given a trait that can verify whether a class is an instantiation of a class template:

#include <type_traits>

template<typename T, template<typename> class TT>
struct is_instantiation_of : std::false_type { };

template<typename T, template<typename> class TT>
struct is_instantiation_of<TT<T>, TT> : std::true_type { };

Use it as follows in your program:

template<typename T>
struct foo {};

template<typename FooType>
struct bar {
  static_assert(is_instantiation_of<FooType, foo>::value, "failure");
};

int main()
{
    bar<int> b; // ERROR!
    bar<foo<int>> b; // OK!
}

If you want, you could generalize this to detect whether a class is an instance of a template with any number of (type) parameters, like so:

#include <type_traits>

template<template<typename...> class TT, typename T>
struct is_instantiation_of : std::false_type { };

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

template<typename FooType>
struct bar {
  static_assert(is_instantiation_of<foo, FooType>::value, "failure");
};

You would then use it this way in your program:

template<typename FooType>
struct bar {
  static_assert(is_instantiation_of<foo, FooType>::value, "failure");
};

int main()
{
    bar<int> b; // ERROR!
    bar<foo<int>> b; // OK!
}

Here is a live example.

Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
  • Do you (or others here) think that the knowledge from reading the book "C++ Templates: The Complete Guide" by Vandevoorde and Josuttis would have allowed me to do this on my own? – roger.james Jun 30 '13 at 13:24
  • Hmm, probably not, since `std::true_type` is a C++11 feature :( There don't seem to be any new template books around for C++11. – roger.james Jun 30 '13 at 13:24
  • 1
    @roger.james: That is a good book and I advise reading it anyway. However, as you mentioned, in this answer I used C++11 features that are not presented in that book (especially variadic templates). – Andy Prowl Jun 30 '13 at 13:28
  • Is it possible to generalize it even further to make type aliases succeed? Example of how it currently fails: https://gist.github.com/anonymous/d318fcac90651bcffa5e – roger.james Jun 30 '13 at 15:38
  • There are two options here: 1) All aliases are equivalent to each other. 2) If the original uses foo_alt, only foo_alt (and not foo) will succeed in the static_assert, and vice versa. Can both versions be accomplished? – roger.james Jun 30 '13 at 15:41
  • @roger.james: First of all, this is an unrelated question :) Please post a new question and accept this answer if it answers the question you posted. Secondly, I think the problem here is that `foo_alt` is *not* a class template, it is a template alias. I am actually surprised that it can be used as a template *template-argument*. (continues...) – Andy Prowl Jun 30 '13 at 15:55
  • @roger.james: In [this example](http://coliru.stacked-crooked.com/view?id=529ded1ad759c6854f6ccef85f6afab2-a63ad9fea3739cb794a74965e5d28fb3), for instance, you can see how the static assert fires even if the input type is an instantiation of `foo_alt`, while the assertion `static_assert(is_instantiation_of::value, "failure")` does not fire even if the type is an instantiation of `foo_alt` (because `foo_alt` is basically an alias for `foo`). – Andy Prowl Jun 30 '13 at 15:56
  • I have accepted the answer and posted another question: http://stackoverflow.com/questions/17392621/using-a-template-alias-instead-of-a-template-within-a-template – roger.james Jun 30 '13 at 16:47
  • Would it be hard to extend this such that *subclasses* of `foo` would also be allowed? – roger.james Jun 30 '13 at 22:45
  • @roger.james: I guess it could be done, yes, but I would have to try and play with it a bit in order to be sure. Anyway, sounds like another good question for SO ;) – Andy Prowl Jun 30 '13 at 23:04
  • I created a new question again: http://stackoverflow.com/questions/17395714/extending-this-template-static-assert-code-to-cover-subclasses – roger.james Jun 30 '13 at 23:16
  • By the way, the live example is broken. – ljleb May 08 '20 at 06:19
3

Some small improvements over the other answers:

  • the name actually makes sense regarding the order of the parameters
  • handles const, volatile, and reference types properly via std::decay
  • implements C++14-style _v constexpr variable
  • accepts an arbitrary number of types to test against (tests for ALL)

I have intentionally not put the std::decay_t on the is_template_for_v because a type trait should work identically regardless of whether it is called with the _v suffix or not.

This does require C++17 for std::conjunction, but you can either remove the variadic feature or implement your own conjunction using c++11/14.

template<template<class...> class tmpl, typename T>
struct _is_template_for : public std::false_type {};

template<template<class...> class tmpl, class... Args>
struct _is_template_for<tmpl, tmpl<Args...>> : public std::true_type {};

template<template<class...> class tmpl, typename... Ts>
using is_template_for = std::conjunction<_is_template_for<tmpl, std::decay_t<Ts>>...>;

template<template<class...> class tmpl, typename... Ts>
constexpr bool is_template_for_v = is_template_for<tmpl, Ts...>::value;

Usage:

static_assert(is_template_for_v<std::vector, std::vector<int>>); // doesn't fire
xaxxon
  • 19,189
  • 5
  • 50
  • 80
-2

As someone else wrote,

template<typename T, template<typename...> class TT>
struct is_specialization_of : std::false_type { };

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

However, beware that this works only for template classes whose template parameters are all typenames! Presented with

typedef std::array<int, 42> MyArray;
static_assert(is_specialization_of<MyArray, std::array>::value, "");

it will simply fail to compile at all.

I believe C++11/C++14/C++17 currently have no way to deal with this limitation.

Quuxplusone
  • 23,928
  • 8
  • 94
  • 159
  • 1
    This is not an answer. Copy/pasting someone else's answer and then commenting on it is not an appropriate answer. If you have a comment on someone's answer, leave a comment on their answer. – xaxxon Oct 29 '17 at 07:33
  • 3
    Who is "someone else"? Please update this answer to provide proper [attribution](https://stackoverflow.blog/2009/06/25/attribution-required/) to whomever you've copied this code from. Was it [Xeo's answer here](https://stackoverflow.com/a/12919396), or another answer? – Cody Gray - on strike Oct 29 '17 at 10:26
  • Welcome to 2017, you two! :) Feel free to edit "someone else" to "Andy Prowl" if you feel like it. – Quuxplusone Oct 29 '17 at 17:44