Class with two or more template overloads - c++

Suppose I had a class that I wanted to declare with two "overloads", one that accepted 1 template parameter and another that accepted 2 like the pseudo code below:
template <typename I>
class B {
public:
};
template <typename F, typename I>
class B {
};
such that B can be instantiated with only 1 or 2 parameters:
B<int> hello;
B<int, int> hello2;
What is the correct way to do this?

Simple answer:
template <typename A, typename... Args> // ellipsis makes Args a
class B // template parameter pack
{
public:
};
int main()
{
B<int> hello;
B<int, int> hello2;
B<int, int, double> hello3;
}
Now Args is called a template parameter pack.
More specific to what you want:
template <typename F, typename I = int>
class B
{
public:
};
int main()
{
B<int> hello;
B<int, int> hello2;
}
This one allows a maximum of two template arguments.

Related

Issue with variadic template template parameter pack

Consider the following example:
template< class A, int B, class C>
struct Obj {
A a_obj;
static constexpr int b_value = B;
C c_obj;
};
template< template<class... > class ... Templates >
struct Foo;
template<>
struct Foo<Obj> {
Obj<void*, 5, float> obj;
};
int main() {
Foo<Obj> foo;
};
This is failing on me (g++ 7.2, with -std=c++17 with the following errors:
templ.cpp:16:15: error: type/value mismatch at argument 1 in template parameter list for ‘template<template<class ...> class ... Templates> struct Foo’
16 | struct Foo<Obj> {
| ^
templ.cpp:16:15: note: expected a template of type ‘template<class ...> class ... Templates’, got ‘template<class A, int B, class C> struct Obj’
templ.cpp: In function ‘int main()’:
templ.cpp:23:8: error: type/value mismatch at argument 1 in template parameter list for ‘template<template<class ...> class ... Templates> struct Foo’
23 | Foo<Obj> foo;
| ^
templ.cpp:23:8: note: expected a template of type ‘template<class ...> class ... Templates’, got ‘template<class A, int B, class C> struct Obj’
It seems that it is not getting matched, although it doesn't look like it thinks the template<class ...> class ... Templates is a syntax error, hence the parameter-parameter pack is parsing
Is this type of declaration (with some types, a constant value, then more types, or perhaps constant values at the end) unmatchable with variadic template template pack syntax? or perhaps I'm not using the syntax right
Edit
I modified the version to avoid non-types in declarations, but the problem persists:
template< template <class, class> class B, class A, class C>
struct Obj {
A a_obj;
//static constexpr int b_value = B;
B< C, A > b_obj;
C c_obj;
};
template< template<typename... > typename ... Templates >
struct Foo;
template<class A, class V>
struct Bar {
A a_obj;
//static constexpr int value = V;
V obj;
};
template<>
struct Foo<Obj> {
Obj<Bar, void*, float> obj;
};
int main() {
Foo<Obj> foo;
};
So it seems that is not that just non-types and types cannot be matches on a variadic pack, but any mixture of types and templates
I also tried adding an additional variadic pack:
template< template<typename... > typename ... Templates, class ... Types >
struct Foo;
But then I get:
error: parameter pack ‘template<class ...> class ... Templates’ must be at the end of the template parameter list
12 | template< template<typename... > typename ... Templates, class ... Types >
int B is a non-type template parameter. You have to declare Templates as template<class, auto, class> class if you want it to work with that particular class template, or redefine Obj to take a std::integral_constant to pass a value encapsulated in a type:
template<class A, class B, class C>
struct Obj {
A a_obj;
static constexpr int b_value = B::value;
C c_obj;
};
template<template<class...> class... Templates>
struct Foo;
template<>
struct Foo<Obj> {
Obj<void*, std::integral_constant<int, 5>, float> obj;
};
int main() {
Foo<Obj> foo;
}
Try it on godbolt.org.

How to check if a type is instantiated from a certain variadic template class in a template function?

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?
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

What's the point of unnamed non-type template parameters?

According to the reference, the name of a non-type template parameter is optional, even when assigning a default value (see (1) and (2)). Therefore these template structs are valid:
template <int> struct Foo {};
template <unsigned long = 42> struct Bar {};
I haven't seen a possibility of accessing the values of the non-type parameters.
My question is: What's the point of unnamed/anonymous non-type template parameters? Why are the names optional?
First, we can split declaration from definition.
So name in declaration is not really helpful. and name might be used in definition
template <int> struct Foo;
template <unsigned long = 42> struct Bar;
template <int N> struct Foo {/*..*/};
template <unsigned long N> struct Bar {/*..*/};
Specialization is a special case of definition.
Then name can be unused, so we might omit it:
template <std::size_t, typename T>
using always_t = T;
template <std::size_t ... Is, typename T>
struct MyArray<std::index_sequence<Is...>, T>
{
MyArray(always_t<Is, const T&>... v) : /*..*/
};
or used for SFINAE
template <typename T, std::size_t = T::size()>
struct some_sized_type;
What's the point of unnamed/anonymous non-type template parameters?
I can think of specialization:
template<int = 42>
struct Foo{
char x;
};
template<>
struct Foo<0> {
int x;
};
template<>
struct Foo<1> {
long x;
};
Then:
Foo<0> a; // x data member is int
Foo<1> b; // x data member is long
Foo<7> c; // x data member is char
Foo<> d; // x data member is char
Oh, you can access them!
template <int> struct Foo {};
template <int N>
int get(Foo<N>) {
return N;
}
int main() {
Foo<3> foo;
return get(foo);
}
This might be a bit contrived. But in general for some templates you don't want to name them and then it is convenient that you don't have to.
Unamed type and non-type parameters also allow you to delay type instanciation, using template-template parameters.
Take destination_type in the function below for instance.
It can resolve to any type that has a template-type parameter, and 0 to N template-values parameters.
template <template <typename, auto...> typename destination_type, typename TupleType>
constexpr auto repack(TupleType && tuple_value)
{
return [&tuple_value]<std::size_t ... indexes>(std::index_sequence<indexes...>) {
return destination_type{std::get<indexes>(tuple_value)...};
}(std::make_index_sequence<std::tuple_size_v<TupleType>>{});
}
static_assert(repack<std::array>(std::tuple{1,2,3}) == std::array{1,2,3});
Such mechanic comes handy when you need an abstraction on parameters-pack.
Here, for instance, we do not care if Ts... is a parameter-pack containing multiple arguments, or expand to a single type which itself has multiples template parameters.
-> Which can be transposed from template-type parameters to template-value parameters.
See gcl::mp::type_traits::pack_arguments_as_t
Complete example available on godbolt here.
template <template <typename ...> class T, typename ... Ts>
class pack_arguments_as {
template <template <typename...> class PackType, typename... PackArgs>
constexpr static auto impl(PackType<PackArgs...>)
{
return T<PackArgs...>{};
}
template <typename... PackArgs>
constexpr static auto impl(PackArgs...)
{
return T<PackArgs...>{};
}
public:
using type = decltype(impl(std::declval<Ts>()...));
};
template <template <typename ...> class T, typename ... Ts>
using pack_arguments_as_t = typename pack_arguments_as<T, Ts...>::type;
namespace tests
{
template <typename... Ts>
struct pack_type {};
using toto = pack_arguments_as_t<std::tuple, pack_type<int, double, float>>;
using titi = pack_arguments_as_t<std::tuple, int, double, float>;
static_assert(std::is_same_v<toto, titi>);
static_assert(std::is_same_v<toto, std::tuple<int, double, float>>);
static_assert(std::is_same_v<pack_type<int, double, float>, pack_arguments_as_t<pack_type, toto>>);
}

Deduce template parameter pack from function call

I have the following code, where I have a template class, and a type in it, which I would like to use in a separate template function.
template <typename... Types>
struct MyClass
{
enum SomeEnum { value0 = -1 };
};
template <typename... Types>
struct OtherClass
{
};
template <typename T, typename... Types>
T check(typename MyClass<Types...>::SomeEnum value)
{
OtherClass<Types...> obj;
T result;
// calculate result from obj;
return result;
}
int main() {
auto value = MyClass<int, bool>::value0;
// ...
int t = check<int>(value);
}
I tought that the compiler will be able to deduce the parameter pack from the function call, so I can use it in the function template also. Unfortunately the compiler can't deduce it:
$ g++ -std=c++11 op.cpp
op.cpp: In function ‘int main()’:
op.cpp:25:27: error: cannot convert ‘MyClass<int, bool>::SomeEnum’ to ‘MyClass<>::SomeEnum’ for argument ‘1’ to ‘T check(typename MyClass<Types ...>::SomeEnum) [with T = int; Types = {}; typename MyClass<Types ...>::SomeEnum = MyClass<>::SomeEnum]’
int t = check<int>(value);
Is there a solution to "transfer" the template parameter pack to the template function?
Template deduction is not possible, but maybe you can restructure your code in a way that MyClass defines the all necessary types and then you have a check function that takes MyClass as a template argument. That way, the checking function has access to all the necessary types.
template <typename... Types> struct OtherClass {};
template <typename... Types>
struct MyClass
{
typedef OtherClass<Types...> OtherClass_t;
typedef int result_t;
enum SomeEnum { value0 = -1 };
};
// version 1
template < typename C >
struct Checker {
typename C::result_t operator()(typename C::SomeEnum value)
{
typename C::OtherClass_t obj;
typename C::result_t result;
// calculate result from obj;
return result;
}
};
// version 2
template < typename C >
typename C::result_t check_fun(typename C::SomeEnum value)
{
typename C::OtherClass_t obj;
typename C::result_t result;
// calculate result from obj;
return result;
}
int main() {
typedef MyClass< int, bool > myclass_t;
auto value = myclass_t::value0;
// ...
Checker< myclass_t > check;
int t = check(value);
auto s = check_fun<myclass_t>(value);
}
The downside is of course, that you have to instantiate the checker class or call the function with the proper type of MyClass as template argument.
Template arguments cannot be deduced from nested types. This isn't new or changed with variadic templates.
The template parameter pack can be passed over using std::tuple. A wrapper class needed over SomeEnum type to store the parameter pack by creating a tuple type from them:
template <typename... Types>
struct MyClass
{
struct Value {
enum SomeEnum { value0 = -1 };
enum SomeEnum value;
typedef std::tuple<Types...> TypeTuple;
};
};
Than a helper class needed which feeds the template argument list of a template class from the tuple types:
template <
template <typename...> class Class,
typename Tuple, typename T, T... nums>
struct Helper_ : Class <
typename std::tuple_element<nums, Tuple>::type... >
{};
template <template <typename...> class Class, typename Tuple>
struct Helper : Helper_<
Class, Tuple,
make_integer_sequence<int, std::tuple_size<Tuple>::value > >
{};
The check function then uses this helper class to instanciate the other class:
template <typename T, typename V>
T check(V value)
{
Helper<OtherClass, typename V::TypeTuple> obj;
T result;
// calculate result from obj;
return result;
}
And the use of check function changes a bit becouse now it waits the wrapper type instead of the pure enum:
int main() {
MyClass<int, bool, double>::Value value;
value.value = MyClass<int, bool, double>::Value::value0;
int t = check<int>(value);
}

template template syntax problems with variadic templates

I have a problem with variadic template templates:
template <typename T> class A { };
template< template <typename> class T> class B { };
template <template <typename> class T, typename parm> class C { typedef T<parm> type; };
template <typename... types> class D { };
template <template <typename...> class T, typename ... parms> class E { typedef T<parms...> type; };
// How to pass list in list??
template < template <typename...> class ...T, ???>
class F
{
};
First, pass a type to the template, no problem:
A<int> a; //ok
Now, I want to create an instance from B, but no way to pass the template template param:
B<A> b; // ok, but no chance to submit <int> inside A!
So I have to extend the parameter list:
C<A, int> c; // ok, this transport int as parm into A
Now I play with variadic templates in standard fashion:
D<> d1; // ok
D<int, float, double> d2; //ok
Passing parameters into the variadic part is also strait forward:
E<D> e1; //ok
E<D, double, float, int> e2; //ok
BUT: If I want to have a list of lists, I find no syntax which will me enable to pass
parameter lists to the list of types. What my intend is something like this. but also the above example shows that B<A<int>> b; is an error! So the following example couldn't work :-(
F< D< int, float>, D< int>, D <float, float, float> > f;
My target is to unroll the list of lists via template specialization. Any hints?
My solution after I understood the problem. Thanks!
Now I can unroll my variadic variadic template template as in the following example. The simple problem was, that I wait for a template class and not for a simple type. Sometimes, the solution can be so easy :-)
Thats my working result now:
template <typename ... > class D;
template <typename Head, typename... types>
class D<Head, types...>
{
public:
static void Do() { cout << "AnyType" << endl; D<types...>::Do(); }
};
template<>
class D<>
{
public:
static void Do() { cout << "End of D" << endl; }
};
template < typename ...T> class H;
template < typename Head, typename ...T>
class H<Head, T...>
{
public:
static void Do()
{
cout << "unroll H" << endl;
cout << "Subtype " << endl;
Head::Do();
H<T...>::Do();
}
};
template <>
class H<>
{
public:
static void Do() { cout << "End of H" << endl; }
};
int main()
{
H< D<int,int,int>, D<float, double, int> >::Do();
return 0;
}
You can unpack one variadic specialisation at a time through specialisation:
template<typename...> struct S;
template<> struct S<> { constexpr static int n = 0; };
template<template<typename...> class T, typename... Us, typename... Vs>
struct S<T<Us...>, Vs...> {
constexpr static int n = sizeof...(Us) + S<Vs...>::n;
};
template<typename...> struct D {};
#include <iostream>
int main() {
std::cout << S<D<int, int, int>, D<int, int>, D<int>>::n << '\n'; // prints 6
}
Like this :
template < template <typename...> class ...T >
class F
{
};
int main()
{
F< D, D > f;
}
So, what F is expecting is a variadic template class, that takes another variadic template class as it's argument.
You can not expand the arguments of the arguments of the template class argument. If you specialize the template class that you pass as argument, then you'll get the specialized version.
This :
F< D< int, float>, D< int>, D <float, float, float> > f;
doesn't work, since F expects variadic template class, taking variadic template classes as types, and D< int, float> is not a template anymore (it is a concrete class).