1

Let's say I have:

template <typename...>
class Foo { };

Then I define another function:

template <typename... T>
void Bar(const T&... arguments) { }

How do I check if all Ts passed to Bar are all instantiated from Foo? Like:

Foo<int> a;
Foo<int, int> b;
Bar(a, b); // OK
int c;
Bar(a, b, c); // ill-formed

I want a way to detect ill-formed arguments like Bar(a, b, c); Is there a way to do this?

palapapa
  • 573
  • 2
  • 5
  • 25

1 Answers1

2

You can create a trait to test for instantiations of Foo, and then fold it across all your parameters.

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

template <typename... Ts>
struct is_foo<Foo<Ts...>> : std::true_type {};

template <typename T>
constexpr bool is_foo_v = is_foo<T>::value;

template <typename... T>
void Bar(const T&... arguments) {
    static_assert((is_foo_v<T> && ...), "arguments must all be Foos");
}

See it on coliru

Caleth
  • 52,200
  • 2
  • 44
  • 75
  • If I'm understanding this correctly, if `T` is `Foo`, the `constexpr bool` evaluates to `constexpr bool is_foo_v = is_foo>::value;`, then does the compiler chooses the second `is_foo` since it's kind of "more valid"? Also, I don't think the second `is_foo` needs to be a variadic template struct or am I missing something? – palapapa Apr 29 '21 at 13:09
  • 1
    The specialisation matches all `Foo` instantiations, and nothing else. It copies the template parameters of `Foo` – Caleth Apr 29 '21 at 13:10
  • When `T` is `Foo`, does the second `is_foo` evaluates to `struct is_foo>>` or `struct is_foo>`? – palapapa Apr 29 '21 at 13:49
  • 1
    @palapapa look at [Partial Specialisation](https://en.cppreference.com/w/cpp/language/partial_specialization), When `is_foo>` is compiled, the specialisation that inherits `true_type` is matched, with `Ts...` equal to `int` – Caleth Apr 29 '21 at 14:00
  • Why is the second `is_foo` a variadic template though? Even if I write something like `is_foo_v>`, the `T` is `Foo` which is still a single type. Is this because that the specialization will only take the `int, int` part of `Foo` so it needs to be variadic? – palapapa Apr 29 '21 at 14:37
  • 1
    Yes, the `Ts...` is to match `int, int` in `Foo` etc, There is no `T` in the `is_foo` template. There is an un-named template parameter in the primary template, and the partial specialisation matches any instantiation of `Foo` there – Caleth Apr 29 '21 at 15:13
  • 1
    You could have a further specialisation, e.g. for `Foo` with at least one `int` parameter `template struct is_foo /* something*/` – Caleth Apr 29 '21 at 15:20