template of template syntax - c++

I am at a bit of a loss with templates of templates.
#include <iostream>
template <class T>
class Foo{
T foobar;
T getFooBar();
};
template <class T>
T Foo<T>::getFooBar(){
return (T) 2.0;
}
template <class T, template<class> class H>
class Bar{
void barbar();
};
template <class T, template<class> class H>
void Bar<T, H<T> >::barbar(){}
int main(int iargc, char *iargv[]){
// do stuff;
}
This does not compile and I do not understand the error message of the compiler
expected a class template, got ‘H<T>’

The correct syntax would be to just pass H:
template <class T, template<class> class H>
void Bar<T, H>::barbar() {}
^^
H<T> is a concrete type, H by itself is the class template you're looking for.

H<T> is itself a type, so you don't need to prefix template<class> to the template parameter for Bar.
Here is a simplified example:
template<class A>
class T1 {
A a;
};
template<class B>
class T2 {
B a;
};
class T3 {
T1<T2<int> > example;
};

If you want to define the member function barbar(), you should
template <class T, template<class> class H>
void Bar<T, H>::barbar(){
// use the type H<T> here
};

Related

Prevent templated class from using itself as instance

Suppose I have a class template
template<class T>
class Foo{};
Is it possible to prevent T from being an instantiation of Foo. That is, this should not compile:
struct Bar{};
Foo<Foo<Bar>> x;
Another option:
#include <type_traits>
template <typename T>
struct ValidFooArg;
template <typename T>
requires ValidFooArg<T>::value
class Foo
{
};
template <typename T>
struct ValidFooArg : std::true_type {};
template <typename T>
struct ValidFooArg<Foo<T>> : std::false_type {};
int main()
{
Foo<int> x; // ok
Foo<Foo<int>> y; // error
}
You might still provide partial specialization to have error in that case:
template <typename T>
constexpr bool always_false = false;
template<class T>
class Foo<Foo<T>>
{
static_assert(always_false<T>);
};

If template parameter AA is a templatized class A<T> itself, is it possible to get the template parameter (T) of this templatized class?

Consider the code below:
template <typename T>
class A{
...
}
template <class U>
class B{
...
}
int main {
B<A<int>> a;
...
}
How can I get the template parameter of A (int in this case) inside B, if A<int> is the template parameter for B?
I could parametrize B as follows, but I feel like I am sending an unnecessary piece of information.
template <class AA, typename T>
class B { ... }
The reason I do not simply use template <typename T> for class B is that I have a pointer to class A inside B, and I want to use the template parameter class AA to see if that pointer is const or not, hence have the correct type for the member pointer in B.
There are several ways, depending of that you might change:
Quick way, specialize B
template <class> class B;
template <class T>
class B<A<T>>
{
// Use directly T
//...
};
Add info in A directly (as std containers do with value_type)
template <typename T>
struct A
{
using my_type = T;
};
// Then in `B<U>`, use `typename U::my_type`
Use external traits to extract information from A (as std::iterator_traits) (that also allows to handle built-in types):
template <typename T>
struct ATrait;
template <typename T>
struct ATrait<A<T>>
{
using my_type = T;
};
// Possibly a generic one
template <template <typename> class C, typename T>
struct ATrait<C<T>>
{
using my_type = T;
};
// Then in `B<U>`, use `typename ATrait<U>::my_type`
I think the following does what you want:
#include<type_traits>
template<class>
class A{};
template<class>
struct get_inner;
template<template<class> class TT, class T>
struct get_inner<TT<T>> {
using outer = TT<T>;
using inner = T;
};
template<class TT>
struct B {
using A = TT;
using inner = typename get_inner<std::decay_t<A>>::inner;
};
int main(int argc, char *argv[])
{
static_assert(std::is_const_v<typename B<const A<int>>::A>);
static_assert(!std::is_const_v<typename B<A<int>>::A>);
}
Note the std::decay_t, it wouldn't work with the const parameter directly (hence we cannot just specialize B in this way). Maybe decay_t is a bit strong but it works^^
Try this
template <typename X> class B;
template <template <typename> class XX, typename T>
class B<XX<T>>
{
// your implementation
};
B<A<int>> a;

Can we introuduce aliases for template specializations?

Can we introduce an alias for a partial specialization? I mean something like that:
template <class T, class V>
class A{ };
typedef template <class T> A<T, int> MyPartialSpecializa<T>;
int main(){ }
DEMO
But it doesn't work. What does the Standard say about that?
You need an alias template for this
template <class T, class V>
class A{ };
template <class T>
using MyPartialSpecializa = A<T, int>;
int main()
{
MyPartialSpecializa<double> a;
}

Template weirdness

I have five classes, declared so:
template <typename T>
class A {
void fn(X);
};
template <typename T>
class B {};
class C {};
class D {};
class X {};
and I have two instances declared so:
A<B<C>> abc;
A<B<D>> abd;
How can I templatize fn so that one must call abc.fn() with an object of type C and abd.fn() with an object of type D?
You can do a partial specialization of your class like this:
template <typename T> class A;
template <typename T> class B {};
template <typename T>
class A<B<T> > {
public:
void fn(T) { }
};
class C {};
class D {};
int main(int,char**)
{
A<B<C>> abc;
A<B<D>> abd;
abc.fn(C());
abd.fn(D());
return 0;
}
If you want it to work for any template, and not just B, you can partially specialize class A like this:
template <typename T,template <typename> class U>
class A<U<T> > {
public:
void fn(T) { }
};
This is not going to be too pretty.
template <typename T>
class B {public: typedef T type;};
template <typename T>
class A {
void fn(typename T::type X);
//void fn(...){} // would prevent an error if T does not have type.
};
Basically you save the type in a typedef and then use that in A. This would error out of course if B does the T of A does not have T::type.

Reducing number of template arguments for class

I have a method and two classes defined like this:
template<template<class X> class T>
void doSomething()
{
T<int> x;
}
template <class T>
class ClassWithOneArg
{
T t;
};
template <class T1, class T2>
class ClassWithTwoArgs
{
T1 t1;
T2 t2;
};
I can now
doSomething<ClassWithOneArg>();
but I cannot
doSomething<ClassWithTwoArgs>();
However, I'd like to pass ClassWithTwoArgs to doSomething, where T2 = double.
The only method I found is to create
template <class T1>
class ClassWithTwoArgs_Child
: public ClassWithTwoArgs<T1, double>
{
};
and then
doSomething<ClassWithTwoArgs_Child>();
This works, but in my concrete case all classes require a constructor argument and thus I have to create a constructor with this argument also in the _Child-class and pass it to the base which I really want to avoid.
Do you have an idea how to do that?
Thanks a lot!
Indirection is a solution. Instead of a template template parameter you pass a "meta function" -- a function that maps one type to another in form of a struct with a nested class template:
struct mf1 {
template<class Arg1>
struct eval {
typedef ClassTemplateWithOneArg<Arg1> type;
};
};
template<class Arg2>
struct mf2 {
template<class Arg1>
struct eval {
typedef ClassTemplateWithTwoArgs<Arg1,Arg2> type;
};
};
template<class MetaFunc>
void do_something()
{
typedef typename MetaFunc::template eval<int>::type clazztype;
clazztype x;
}
void foo() {
do_something<mf1>();
do_something<mf2<double> >();
}
In C++0x this could be reduced to a "template typedef":
template<class Arg1>
using NewClassTemplate = ClassTemplateWithTwoArgs<Arg1,double>;
which allows you to pass NewClassTemplate as a template template argument which also accepts only one template parameter.
There is no generic solution. Your best bet is
template<class T>
void doSomething()
{
T x;
}
template <class T>
class ClassWithOneArg
{
T t;
};
template <class T1, class T2 = double>
class ClassWithTwoArgs
{
T1 t1;
T2 t2;
};
int main(){
doSomething<ClassWithOneArg<int>>();
doSomething<ClassWithTwoArgs<int, double> >();
}
It seems that what you are after is similar to the rebinding of allocators (given an allocator, containers need to be able to produce an allocator for a different type - e.g std::list<int> might need a allocator<list_node<int> > from allocator<int>.
However, the class templates would have to be modified for this.
template<class T>
void doSomething(const T&)
{
typename T::template rebind_1st<int>::type x;
}
template <class T>
class ClassWithOneArg
{
T t;
public:
template <class U>
struct rebind_1st { typedef ClassWithOneArg<U> type; };
};
template <class T1, class T2>
class ClassWithTwoArgs
{
T1 t1;
T2 t2;
public:
template <class U>
struct rebind_1st { typedef ClassWithTwoArgs<U, T2> type; };
};
int main()
{
doSomething(ClassWithOneArg<char>());
doSomething(ClassWithTwoArgs<char, double>() );
}
Assuming you want declare template instantiations of the same class with a different type for the first template parameter, it appears a version of visitor's code is possible that doesn't require modifying original classes.
template <class T, class NewFirstArg>
struct rebind_1st;
template <template <class> class T, class Arg1, class NewFirstArg>
struct rebind_1st<T<Arg1>, NewFirstArg>
{
typedef T<NewFirstArg> type;
};
template <template <class, class> class T, class Arg1, class Arg2, class NewFirstArg>
struct rebind_1st<T<Arg1, Arg2>, NewFirstArg>
{
typedef T<NewFirstArg, Arg2> type;
};
template <class T>
void foo()
{
typename rebind_1st<T, int>::type x;
(void)x;
}
template <class T>
struct One{};
template <class T1, class T2>
struct Two{};
int main()
{
foo<One<char> >();
foo<Two<char, double> >();
}
This works with MSVC:
template<class T>
void doSomething()
{
T x;
}
// class definitions omitted...
void test() {
doSomething<ClassWithOneArg<int> >();
doSomething<ClassWIthTwoArgs<int, double> >();
}
I do not fully understand why you want to define the first parameter of your template template parameter to be int inside of doSomething. Looks like a "template smell" to me, since doSomething has to know a lot about its template template parameter.
Wouldn't it be cleaner to call doSomething the way i proposed? (But obviously i don't know the context of your calls).