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.
Related
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.
I am trying to use std::conditional to implement static inheritance.
I have two possible parents of my child class, parent_one, which should hold multiple variables based on passed types, and parent_two, which takes two types. I am using tag dispatching to differ between the classes I want to inherit from.
Now to the problem. When I am calling child and tagging it to inherit from parent_one with two types, it works as intended. However, if I try to pass any number of types into the child with the intention of inheriting from parent_one, I get error:
static_polymorphism.cpp: In instantiation of ‘class child<foo_type, int, int, double, float>’:
static_polymorphism.cpp:110:41: required from here
static_polymorphism.cpp:99:7: error: wrong number of template arguments (4, should be 2)
99 | class child : public std::conditional_t<
| ^~~~~
static_polymorphism.cpp:90:7: note: provided for ‘template<class T, class F> class parent_two’
90 | class parent_two {
| ^~~~~~~~~~
static_polymorphism.cpp: In function ‘int main(int, char**)’:
static_polymorphism.cpp:111:9: error: ‘class child<foo_type, int, int, double, float>’ has no member named ‘log’
111 | first.log();
If I understand that correctly the compiler should generate code based on my tag dispatching. That means it should create overloaded classes - N(Based on types passed) from parent_one and M(Based on types passed as well) from parent_two. However for some reason, unknown to me, it is not accepting the variable count of types. Could you tell me what am I doing wrong please?
Implementation is here.
using one_t = struct foo_type{};
using two_t = struct bar_type{};
template <typename ... TYPES>
class parent_one {
public:
parent_one() = default;
void log() {
std::cout << "parent_one" << std::endl;
}
};
template <typename T, typename F>
class parent_two {
public:
parent_two() = default;
void log() {
std::cout << "parent_two" << std::endl;
}
};
template <typename T, typename ... ARGS>
class child : public std::conditional_t<
std::is_same_v<T, one_t>,
parent_one<ARGS...>,
parent_two<ARGS...>
>
{
public:
child() = default;
};
int main(int argc, char *argv[]) {
child<one_t, int, int, double, float> first;
first.log();
child<two_t, int, int> second;
second.log();
return 0;
}
std::conditional_t<
std::is_same_v<T, one_t>,
parent_one<ARGS...>,
parent_two<ARGS...>
>
Here both alternatives are validated before the condition is checked. std::conditional_t is not magical, being just a regular template it requires all template arguments to be valid before it can do anything.
You need to delay the substitution of template arguments into a parent template until after one of the alternatives is selected. Here's one possible solution:
template <template <typename...> typename T>
struct delay
{
template <typename ...P>
using type = T<P...>;
};
// ...
class child :
public std::conditional_t<
std::is_same_v<T, one_t>,
delay<parent_one>,
delay<parent_two>
>::template type<ARGS...>
{
// ...
};
You may be classic:
template<class T, class... Args> struct child: parent_one<Args...> {};
template<class T, class A, class B> struct child<T, A, B>: parent_two<A, B> {};
template<class A, class B> struct child<one_t, A, B>: parent_one<A, B> {};
(the two specializations can be combined into one with requires (!std::is_same_v<T, one_t>)).
EXAMPLE
template< typename T >
struct A {
};
template< typename T, typename U >
struct A : A<T> {
};
int main() {
A<double> ad;
A<int, int> a;
}
COMPILE ERROR
g++ -std=c++17 -Wall -pedantic -pthread main.cpp && ./a.out
main.cpp:9:8: error: redeclared with 2 template parameters
struct A : A<T> {
^
main.cpp:4:8: note: previous declaration 'template<class T> struct A' used 1 template parameter
struct A {
^
main.cpp: In function 'int main()':
main.cpp:16:5: error: expected initializer before 'A'
A<int, int> aii;
^
Differrent template names works fine:
template< typename T >
struct A {
};
template< typename T, typename U >
struct AA : A<T> {
};
int main() {
AA<int, int> aa;
}
Wanna have same template name. It should be possible with variadic templates but I don't know how.
Thanks for your attention
You can use default parameters if you can define default:
template<typename T, typename U = /* default */>
struct A {
};
If you want to treat different amount of template parameter with different behavior, you can also use variadic templates and specialization for that:
template<typename...>
struct A;
template<typename T>
struct A<T> { // specialization for one parameter
};
template<typename T, typename U>
struct A<T, U> { // specialization for two parameter
};
int main() {
A<double> ad;
A<int, int> a;
// A<int, int, int> a; // error, undefined
}
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);
}
I have a problem that Curiously Recurring Templates could help quite nicely, but I cant even get past a simple test.
template<typename T, int _size, typename OutterT>
class Foo {
};
template<typename T>
class Bar : public Foo<T, 2, Bar> {};
//typedef Bar<float> Vec2f;
int main()
{
return 0;
}
This results in the error
foo.cpp:7: error: type/value mismatch at argument 3 in template parameter list for ‘template<class T, int _size, class OuterT> class Foo’
foo.cpp:7: error: expected a type, got ‘Bar’
What am I missing.
compiled with g++ 4.2.1
template<typename T, int _size, typename OutterT>
class Foo {
};
template<typename T>
class Bar : public Foo<T, 2, Bar<T> > {};
// ^^^
Bar<float> x;
Since Bar is a template, you must provide the template argument to instantiate it into a class.