Specialize class template for variadic parameters - c++

I want to specialize a class template for variadic parameters:
template <typename... Ts>
struct TypeList
{
};
template <typename T>
class Foo
{
};
//Is next "specialization" even possible?
template <typename... Ts>
class Foo<TypeList<Ts...>>
{
};
Foo<TypeList<int, int>> i1{}; // OK
Foo<int, int> i2{}; // NOK: error: wrong number of template arguments (2, should be 1)
I want that users of Foo can choose between providing a TypeList or an explicit list of types.

What you can do, to allow both syntaxs:
template <typename ... Ts>
class Foo : Foo<TypeList<Ts...>>
{
};
// Specialization
template <typename ... Ts>
class Foo<TypeList<Ts...>>
{
// ...
};

Related

Way to deduce if type is from a templated class

I've been trying to answer the question in the title, but I'm stumped. Basically, trying to see if there's a built-in way to tell the 'source' of a template instantiation, at least for classes. Here is an example of what I'd like to do:
template<class T>
class A { };
auto a = A<int>();
template<class T>
auto someFunction(T item) {
if(/* if type of a is from the templated class A */) {
// yep A<int> is 'from' A.
}
}
Is this possible, in some way such as this? I could use some saved value or inheritance shenanigans to get something similar, but I'd rather not.
Maybe with a custom type traits.
Something as follows
template <typename>
struct is_A : public std::false_type
{ };
template <typename T>
struct is_A<A<T>> : public std::true_type
{ };
// ...
template <typename T>
auto someFunction(T item) {
if( is_A<T>::value ) {
// yep A<int> is 'from' A.
}
}
Or, maybe, you want intercept non only A<T> but, generically, all types that are a template type, maybe with an undefined number of template arguments?
In this case you can try with something as follows
template <typename>
struct is_template : public std::false_type
{ };
template <template <typename...> class C, typename ... Ts>
struct is_template<C<Ts...>> : public std::true_type
{ };
Problem: this type traits intercepts all template types with types template arguments and only template arguments. But doesn't intercept, by example, std::integer_sequence<int, 0, 1, 2, 3, 4, 5> that receive a type and some non-type template parameter.
You can add other specializations for is_template, to intercept other cases, but you can't define a specialization that catch all template types (all combinations of template parameters).
Use a type trait
#include <type_traits>
#include <iostream>
template<class T>
class A { };
auto a = A<int>();
template <typename X>
struct is_from_A : std::false_type {};
template <typename T>
struct is_from_A<A<T>> : std::true_type {};
int main() {
std::cout << is_from_A<int>::value << "\n"; // 0
std::cout << is_from_A<A<int>>::value << "\n"; // 1
}
An other way, allowing derived class to match:
template <typename T>
std::true_type is_an_A_impl(A<T>*);
std::false_type is_an_A_impl(...);
template <typename T>
using is_a_A = decltype(is_an_A_impl(std::declval<T*>()));

template template parameter for stl containers behaving different from custom template class

I have the following struct and function
template <class T> struct C {};
template <template <class S> class T, class U> void f() { T<U> tu; }
when templating f() with C I do not get an error, when templating it with say std::vector I do.
int main() {
f<C, int>();
}
yields no errors
int main() {
f<std::vector, int>();
}
yields
error: no matching function for call to 'f'
f<std::vector, int>();
^~~~~~~~~~~~~~~~~~~~~~~~
note: candidate template ignored: invalid explicitly-specified argument for template parameter 'T'
template <template <class S> class T, class U> void f() { T<U> tu; }
What is the difference between C and std::vector here?
That's because vector has two template parameters, not one (T and Allocator).
You can either change your f template to accept two template parameters (or a variadic pack):
template <template <class...> class T, class U> void f() { T<U> tu; }
or you can alias vector to a 1-parameter template:
template<typename T>
using vec = std::vector<T>;
difference is that vector has two template parameters, not one. To fix this you may use
template <template <class... S> class T, class U> void f() { T<U> tu; }

How can I take a variadic std::tuple as a template argument?

Suppose I have a struct
template <typename Args>
struct MyStruct
{
};
But I only want to be able to instantiate this class with std::tuple instantiations, e.g.
Mystruct<std::tuple<>> a; // OK
Mystruct<std::tuple<int, int, double>> a; // OK
Mystruct<double> a; // FAIL
How can I do this?
This is fairly simple. Declare but not define a general template:
template<typename T> struct Mystruct;
then define a specialization:
template<typename... Ts>
struct Mystruct<std::tuple<Ts...>>
{
// stuff
};
In addition to krzaq's answer, to have a better error message, you might want to use static_assert
// The traits:
template <typename T>
struct is_a_tuple : std::false_type {};
template <typename ... Ts>
struct is_a_tuple<std::tuple<Ts...>> : std::true_type {};
// Your structure (for error message)
template <typename T>
struct MyStruct
{
static_assert(is_a_tuple<T>::value, "T should be a std::tuple");
};
// Your structure (for std::tuple)
template <typename ... Ts>
struct MyStruct<std::tuple<Ts...>>
{
// Your implementation
};

Having a template parameter depend on a parameter list

I have defined the class
template <typename... Ts> struct Bar {
using inner_type = /* whatever */;
};
Now, I need to define a templated class Foo whose template parameters are some parameter pack, and a value of type Bar::inner_type instantiated for that parameter pack. Unfortunately I can't seem to be able to do it. If I define it this way:
template <Bar<Ts...>::inner_type SomeValue, typename... Ts> struct Foo { };
the compiler doesn't recognize Ts when it's used, since it hasn't see the parameter pack yet; but if I define it this way:
template <typename... Ts, Bar<Ts...>::inner_type SomeValue> struct Foo { };
the compiler sneers at my attempt to use a parameter pack before other template parameters.
So how can I do this?
Note: In case it matters, this failed for me with GCC 4.9.3.
You can partially specialize your struct:
template<typename...>
struct Bar { using inner_type = int; };
template <typename T, typename T::inner_type T>
struct Foo;
template <typename... Ts, typename Bar<Ts...>::inner_type SomeValue>
struct Foo<Bar<Ts...>, SomeValue> { };
int main() {
Foo<Bar<int>, 3> foo;
}
This way Ts parameter pack is deduced and Foo expects the second template parameter to be of type Bar<Ts...>::inner_type.
The best thing I could come up with:
template <class Inner, Inner Val, class... Args>
struct Foo {
static_assert(std::is_same<Inner, typename Bar<Args...>::inner_type>::value, "Wrong type");
};
You need to explicitly name the type.
does this solve the issue?
#include <type_traits>
using namespace std;
template <typename... Ts> class Bar {
public:
using inner_type = int;
};
template <typename... Ts> class Foo {
using bar_inner_type = typename Bar<Ts...>::inner_type;
static_assert(is_same<int, bar_inner_type>::value,"");
};
If I understood your problem correctly, you can do something like this:
template <typename... Ts> struct Bar {
using inner_type = /* whatever */;
};
template <typename... Ts> struct Foo {
using inner_type = typename Bar<Ts...>::inner_type;
};

Validate template parameter is other defined template

How can I test that OtherFoo template parameter (for my TT alias) is Foo with other template parameters:
template <class... Pack>
class Foo
{
class SomeClass {};
template <class OtherFoo> // OtherFoo = Foo with other template parameters
using TT = typename OtherFoo::SomeClass;
};
It is assumed that it is impossible to do spoofing.
With a partial specialization, like that:
template <class... Pack>
class Foo
{
class SomeClass {};
template <class OtherFoo> // OtherFoo = anything, but undefined
struct TT_t;
template <class... P> // OtherFoo = Foo with other template parameters
struct TT_t <Foo <P...> > { using type = typename Foo <P...>::SomeClass };
template <class OtherFoo> // OtherFoo = Foo with other template parameters
using TT = typename TT_t <OtherFoo>::type;
};
So if you try to instantiate TT with any parameter other than another Foo, the compiler will issue an error, saying that TT_t is an incomplete type. I hope this is what you were after.