C++ use alias template declaration in different classes - c++

I got three template classes (Code in C++ Shell) and an alias template using AType = A<T1,T2> defined in A. I would like to use the alias across the files where objects of A are being used.
So in B instead of writing:
C<A<T1,T2>> c_object;
I'd rather like to be able to write something like:
C<typename AType> c_object;
using the alias declared in A.
// A.h
template<typename T1, typename T2>
class A{
A();
};
template<typename T1, typename T2>
using AType = A<T1,T2>;
// C.h
template<typename H>
// H = A<T1,T2>
class C{
C();
};
// B.h
#include "A.h"
template<typename T1, typename T2>
class B{
B();
// const C<A<T1,T2>>& GetC() const;
const C<typename AType>& GetC() const;
private:
C<typename AType> c_object;
};
Note the relations of the template parameters.
If B would only have one template parameter G like:
template<typename G>
class B{};
I could just do:
C<typename G::AType> c_object;
But since B actually has two, I can't figure out how that would work out. Or is entire problem usually solved in a different way?

You should instead add a using to B for your C type:
#include "A.h"
template<class T1, class T2>
struct B{
using C_type = A<T1, T2>;
B();
// const C_type& GetC() const;
const C_type& GetC() const;
private:
C_type c_object;
};
Or could inject A into the template arguments for B, which would let the caller optionally change it:
#include "A.h"
template<class T1, class T2, template<class, class> class C_T = A<T1, T2>>
struct B{
B();
// const C_T& GetC() const;
const C_T& GetC() const;
private:
C_T c_object;
}
You misunderstand what template aliases are for. They simply make referring to some other template easier; they don't implicitly bind template arguments to be passed on (T1 and T2 don't get implicitly passed to A in your OP; the template arguments to A must be specified).
You could simplify writing a C type templated for A using a template alias:
template<class T1, class T2>
using C_A = C<A<T1, T2>>;
And then you could use C_A<T1, T2> in B. This may be more flexible if you need a C templated on A in more than one location. However, what I wrote above may be preferable; this was only for demonstrated purposes.

The problem is that AType isn't a type; it's a template template type.
Your code compile if you write C as follows
template <template <typename...> class>
class C
{ C(); };
and pass AType in this way
template <typename T1, typename T2>
class B
{
B();
const C<AType>& GetC() const;
private:
C<AType> c_object;
};
but I don't understand what do you want to do with C and AType
-- EDIT --
Klaus say
In your posted code you write C<AType> c_objectwhich is invalid as using AType = A;`. I can't see the place where you put any type to aliased template AType. So your posted code makes no sense as T1 and T2 are unused.
My answer: I suppose that my posted code "makes no sense as T1 and T2 are unused", because I didn't understand the sense of the original code.
But you say my code "is invalid".
The following is my code that use C<AType> c_objectwhich and using AType = A<T1,T2>;. Please, say to me where is invalid and which error you get compiling it.
template <typename, typename>
class A { A(); };
template <typename T1, typename T2>
using AType = A<T1,T2>;
template <template <typename...> class>
class C { C (); };
template <typename, typename>
class B
{
B();
const C<AType> & GetC () const;
private:
C<AType> c_object;
};
int main()
{ }
template <typename, typename>
class A { A(); };
template <typename T1, typename T2>
using AType = A<T1,T2>;
template <template <typename...> class>
class C { C (); };
template <typename, typename>
class B
{
B();
const C<AType> & GetC () const;
private:
C<AType> c_object;
};
int main()
{ }

Related

How to reduce recursive variadic inheritance code bloat?

Let's say I want to create a variadic interface with different overloads for the structs A,B,C:
struct A{};
struct B{};
struct C{};
template <typename ... Ts>
class Base;
template <typename T>
class Base<T>{
public:
virtual void visit(const T& t) const
{
// default implementation
}
};
template<typename T, typename ... Ts>
class Base<T, Ts...>: Base<T>, Base<Ts...>{
public:
using Base<T>::visit;
using Base<Ts...>::visit;
};
int main()
{
A a;
B b;
auto base = Base<A,B,C>{};
auto base2 = Base<A,C,B>{};
base.visit(a);
base2.visit(b);
}
Now funtionally Base<A,B,C> is identical to Base<A,C,B> but the compiler still generates the different combinations. Of course with more template parameters it gets worse.
I assume there is some meta programming magic which can cut this code bloat down.
One solution might be to define template<typename T, typename U> Base<T,U> in a way that it checks if Base<U,T> already exists. This could reduce at least some combinations and can probably be done by hand for triplets as well. But I am missing some meta programming magic and hoping for a more general approach.
Edit:
I would like to have the variadic Interface for a (simplified) use case like that:
class Implementation:public Base<A,B,C>
{
public:
void visit(const A& a) const
{
std::cout <<"Special implementation for type A";
}
void visit(const B& a) const
{
std::cout <<"Special implementation for type B";
}
// Fall back to all other types.
};
using BaseInterface = Base<A,B,C>;
void do_visit(const BaseInterface& v)
{
v.visit(A{});
v.visit(B{});
v.visit(C{});
}
int main()
{
std::unique_ptr<BaseInterface> v= std::make_unique<Implementation>();
do_visit(*v);
}
The reason why I want to do this is that there could be potentially a lot of types A,B,C,... and I want to avoid code duplication to define the overload for each type.
Base<A, B, C> instantiates Base<A>, Base<B, C>, Base<B>, Base<C>
and
Base<A, C, B> instantiates Base<A>, Base<C, B>, Base<B>, Base<C>
Whereas final nodes are needed, intermediate nodes increase the bloat.
You can mitigate that issue with:
template <typename T>
class BaseLeaf
{
public:
virtual ~BaseLeaf() = default;
virtual void visit(const T& t) const
{
// default implementation
}
};
template <typename... Ts>
class Base : public BaseLeaf<Ts>...
{
public:
using BaseLeaf<Ts>::visit...;
};
Demo
Base<A,B,C> and Base<A,C,B> are still different types.
To be able to have same type, they should alias to the same type, and for that, ordering Ts... should be done in a way or another.
Looks like the member function should be a template rather than the class.
struct A{};
struct B{};
struct C{};
class Foo {
public:
template<typename T>
void visit(const T& t) const
{
// default implementation
}
};
int main()
{
A a;
B b;
auto foo = Foo{};
foo.visit(a);
foo.visit(b);
}
https://godbolt.org/z/nTrYY6qcn
I'm not sure what is your aim, since there is not enough details. With current information I think this is best solution (there is a also a lambda which can address issue too).
It's necessary to enforce some sort of discipline on the order of template parameters. You can do this with a template variable and a few static_asserts:
#include <type_traits>
template <typename ... Ts>
class Base;
struct A
{
};
struct B
{
};
struct C
{
};
struct D
{
};
template <class T>
static constexpr int visit_sequence_v = -1;
template <>
constexpr int visit_sequence_v<A> = 0;
template <>
constexpr int visit_sequence_v<B> = 1;
template <>
constexpr int visit_sequence_v<C> = 2;
template <typename T>
class Base<T>{
public:
static_assert(visit_sequence_v<T> >= 0, "specialize visit_sequence_v for this type");
virtual void visit(const T& t) const
{
// do nothing by default
}
};
template<typename T1, typename T2>
class Base<T1, T2>: Base<T1>, Base<T2>
{
public:
static_assert(std::is_same_v<T1, T2> || visit_sequence_v<T1> < visit_sequence_v<T2>);
using Base<T1>::visit;
using Base<T2>::visit;
};
template<typename T1, typename T2, typename ... Ts>
class Base<T1, T2, Ts...>: Base<T1>, Base<T2, Ts...>
{
public:
static_assert(std::is_same_v<T1, T2> || visit_sequence_v<T1> < visit_sequence_v<T2>);
using Base<T1>::visit;
using Base<Ts...>::visit;
};
int main()
{
A a;
B b;
auto base = Base<A,B,C>{};
//auto base2 = Base<A,C,B>{}; // static_assert fails
//auto base3 = Base<A,B,C,D>{}; // forgot to specialize
base.visit(a);
}
Notice the point here is to cause a compilation failure if you get the order wrong. If someone has the chops to do a compile-time sort it may be possible to cobble up a traits class (or a template function that you can use decltype on the return type) that selects an implementation of Base in the correct order.
One alternative is to declare a full specialization of Base for every individual type that can be visited (supplying a "default implementation" for visit) and declare a static constexpr visit_sequence within each specialzation.
A problem inherent in your method is that in the case of multiple inheritance, visit can be ambiguous:
struct E: public A, public B
{
};
// 5 MiNuTES LATeR...
DescendantOfBase<A, B> a_b;
E e;
a_b.visit (e); // ambiguous

Template argument deduction failed with typedef?

Considering the following couple of classes:
template <typename T1, typename T2>
class A{
public:
// ...
};
template<typename _T>
struct alias { typedef A<int,_T> intA; };
class B{
public:
// ...
template <typename _T> B& operator=(const typename alias<_T>::intA& _arg) { };
};
When I try to assign an object of class A<int,int> to an object of class B, I get the following compilation error:
template argument deduction/substitution failed: couldn't deduce template parameter ‘_T’
Is there an alternative way to use something of a typedef as the input argument to B::operator=()??
templated using might fix the issue
template <typename T1, typename T2>
class A{
public:
// ...
};
template<typename _T>
using alias = A<int,_T>;
class B{
public:
// ...
template <typename _T> B& operator=(const alias<_T>& ) { return *this; };
};
void f()
{
B b;
A<int, int> a;
b = a;
}
The problem is that intA is a dependant name. Templates cannot be deduced from dependant names. See for example: Dependent Types: Template argument deduction failed.
You are also missing the typename keyword.
You can either explicitly specify the type for the operator:
template <typename T1, typename T2>
struct A{ };
template<typename _T>
struct alias { typedef A<int,_T> intA; };
struct B
{
template <typename T> B& operator=(const typename alias<T>::intA& _arg) { };
};
int main()
{
A<int,int> a;
B b;
b.operator=<int>(a);
return 0;
}
or you can have a specific, non-dependant-name parameter using a templated alias (with or without a function):
template <typename T1, typename T2>
struct A{ };
template<class T>
using alias_int = A<int, T>;
struct alias
{
template<class T>
using intA = A<int, T>;
};
struct B
{
template <typename T> B& operator=(const alias_int<T>& _arg) { };
};
struct C
{
template <typename T> C& operator=(const alias::intA<T>& _arg) { };
};
int main()
{
A<int,int> a;
B b;
C c;
b = a;
c = a;
return 0;
}
I'm getting a different error (using g++ 5.4):
need ‘typename’ before ‘alias<_T>::intA’ because ‘alias<_T>’ is a dependent scope
and true enough the following compiles for me:
template <typename T1, typename T2>
class A{
public:
// ...
};
template<typename _T>
struct alias { typedef A<int,_T> intA; };
class B{
public:
// ...
template <typename _T> B& operator=(const typename alias<_T>::intA& _arg) { };
};
I think the reason is that alias<_T>::intA isn't an actual type but a templated typename.

specialize template<typename T, template<typename> class U>

I have a problem with nested templates and their template specialization. Given the following classes:
A small template class
template<class U>
class T {
public:
T(){}
virtual ~T (){}
};
And some kind of nested template
template<typename T, template<typename> class U>
class A {
public:
void foo()
{
std::cerr << "A generic foo";
}
};
And a small main.cpp
int main(int argc, const char *argv[])
{
A<int,T> *a = new A<int,T>;
a->foo();
//This wont work:
A<double,T*> *b = new A<double,T*>;
b->foo();
return 0;
}
Now I need a specialization if U is a pointer:
A<double,T*> *b = new A<double,T*>;
b->foo();
How to achieve this? I tried something like:
template<typename T, template<typename> class U>
class A< T, U* >
{
public:
void foo()
{
std::cerr << "A specialized foo";
}
};
But it just resolves in
A.h:18:16: Error: Templateargument 2 is invalid
What you're tying to do is not possible, because T* has no meaning. Neither is it a proper type, nor does it match a template, which requires additional parameters. If U were to represent T*, what would U<int> be? You probably mean T<int>* but that doesn't match your declaration, so there is no way to plug that type into A.
Since you asked for a way to get around this, from the top of my head, something like this.
Accept a third template argument to A, which I would call Expander and set it by default to this:
template <typename T> struct Expander {
typedef T type;
};
Then, when invoking A you could say
A<int,T> normal;
A<int,T,PtrExpander> pointer;
with
template <typename T> struct PtrExpander {
typedef T* type;
};
and A would be:
template<typename T, template<typename> class U, template <typename> class E = Expander> class A {
typedef typename E<U<Your_Args_to_U> >::type;

How can I get my nested class inside a templated class to accept other versions of itself?

The following doesn't compile, how can I do this? I think the example shows my intent, but I'll try to add a blurb if people are confused.
template<typename T>
class A
{
private:
struct B
{
template<typename T2>
B& operator=( const A<T2>::B& right ){} // how can I make this work?
};
template<typename T2> friend class A;
template<typename T2> friend class A<T2>::B;
};
The fundamental idea of your assignment operator is flawed, as even with the addition of a typename, it's a non-deducible context. As such, the template parameter will never be deduced and the assignment operator will never work unless you explicitly specify a type like B.operator=<some_type>(other_B).
An easier version would be to just make it a normal function template, and SFINAE your way out.
#include <type_traits>
template<class> struct is_a_B;
template<class B2>
typename std::enable_if<
is_a_B<B2>::value,
B&
>::type operator=(B2 const& other){
// ...
}
Now all that's left is the is_a_B type trait. You can make this easy on yourself with possible false positives:
template<class B>
struct is_a_B{
typedef char yes;
typedef yes (&no)[2];
template<class T>
static yes test(typename T::I_am_a_B_type*);
template<class T>
static no test(...);
static bool const value = sizeof(test<B>(0)) == sizeof(yes);
};
Just provide the I_am_a_B_type typedef in your B class.
Live example on Ideone. Comment out the b1 = 5; line and it compiles as seen here.
And now for the slightly more perverted complicated way with no false-positives. :)
template<bool Cond, class OnTrue>
struct and_v{
static bool const value = OnTrue::value;
};
template<class OnTrue>
struct and_v<false, OnTrue>{
static bool const value = false;
};
template<class B>
struct is_a_B{
typedef char yes;
typedef yes (&no)[2];
template<class T>
static yes has_parent(typename T::parent*);
template<class T>
static no has_parent(...);
template<class T>
static yes is_A(A<T>*);
static no is_A(...);
template<class T>
struct lazy_test{
typedef typename std::add_pointer<typename T::parent>::type p_type;
static bool const value = sizeof(is_A(p_type(0))) == sizeof(yes);
};
static bool const value = and_v<sizeof(has_parent<B>(0)) == sizeof(yes),
lazy_test<B>>::value;
};
For this one you need a typedef A<T> parent; inside B. It's staged in two parts:
First I test if a parent typedef exists, and if it does
If it's actually a typedef of the A class template.
Sadly, the logical operators (&&, ||, ?:) don't short-circuit in template code like I hoped, so I had to write those and_v templates + a lazy tester that only gets evaluated if a parent typedef exists.
Live example on Ideone. Again, comment out the b1 = 5; line to make it compile as seen here.
It should not work at all.
Here is the reason
template<typename T>
class A
{
private:
struct B
{
template<typename T2>
B& operator=( const typename A<T2>::B& right ){}
};
template<typename T2> friend class A;
template<typename T2> friend class A<T2>::B;
};
template <>
class A<int>
{
};
As you can see there is not A< int >::B class at all!

Infer subclass template types in a template

I have a class that derives from a templated class:
template <typename A,typename B>
class TemplatedClass {
};
class Hello : public TemplatedClass<int,float>
{
};
Now, I want to make a templated class that will infer the types int,float from Hello.
I thought I could do something like this, but it doesn't work:
template <template <typename A,typename B> class C>
class Check
{
void Foo(A,B,C)
{
// A is int .. B is float .. C is Hello
}
};
int _tmain(int argc, _TCHAR* argv[])
{
Check<Hello> a;
}
How can i do this ?
Edit:
I want to pass the class Hello and have the template infer the types used by its subclass TemplatedClass .
So, when I create a class Check<Hello> it will get the types int and float
I can't really change the TemplatedClass to include typedefs (it's from an an external .lib)
Edit:
I've changed the template to use Class , I get this error though:
error C3200: 'Hello' : invalid template argument for template parameter 'C', expected a class template
First, change that typename to class, §14.1 [temp.param] p1:
type-parameter:
class identifieropt
class identifieropt = type-id
typename identifieropt
typename identifieropt = type-id
template <template-parameter-list > class identifieropt
template <template-parameter-list > class identifieropt = id-expression
Next, make a partial specialization:
template<class T>
class Check;
template< // not 'typename' vvvvv
template<typename,typename> class C,
typename A, typename B
>
struct Check<C<A,B> >{
// ...
};
Though, you still can't pass just Hello to the template, because even though Hello derives from TemplatedClass, type conversion is not allowed for template parameters:
Check<Hello> c; // nope, 'Hello' is not a template
You could add the following typedef to the Hello class:
class Hello
: TemplatedClass<int,float>
{
public:
typedef TemplatedClass<int,float> base_type;
};
And do:
Check c; // OK
But then the C parameter in Check will be template TemplatedClass, not Hello. Sadly, there's no way to achieve that directly. One solution is to pass the derived type either as an extra template parameter or just pass the derived type as the only parameter and do the type extraction internally:
template<class T>
class CheckInternal;
template<
template<typename,typename> class C,
typename A, typename B
>
class CheckInternal<C<A,B> >{
public:
typedef A type_A;
typedef B type_B;
};
template<class T>
class Check{
typedef typename T::base_type T_base_type;
typedef typename CheckInternal<T_base_type>::type_A type_A;
typedef typename CheckInternal<T_base_type>::type_B type_B;
void foo(type_A a, type_B b){
// ...
}
};
// usage:
C<Hello> c; // OK!
You could do something like this:
template <template <typename A,typename B> typename C>
class Check
{
template<typename A, typename B>
void Foo(A a, B b) {
// C<A,B> would reconstruct the template type
}
}
// use:
Check<Hello> a;
a.Foo(true,1.f);
or alternatively, this (it's not exactly clear what your intent is):
template <typename A,typename B>
class TemplatedClass {
typedef A TypeA;
typedef B TypeB;
};
template <typename C>
class Check
{
void Foo(typename C::TypeA& a, typename C::TypeB&) {}
}
// use:
Check<Hello<int,float> > a;
a.Foo(1,1.f);
Try something like:
template <typename A,typename B>
class TemplatedClass {
public:
typedef A firstType;
typedef B secondType;
};
class Hello : public TemplatedClass<int,float>
{
public:
typedef firstType Type1;
typedef secondType Type2;
};
template <typename C>
class Check
{
typedef typename C::Type1 A;
typedef typename C::Type2 B;
void Foo(A,B,C)
{
// A is int .. B is float .. C is Hello
}
};
I think you can do it by creating a templated class for Hello and then typedefing a specialization of it. Something like:
template <typename A, typename B>
class HelloBase : public TemplatedClass<A, B>
{
public:
typedef A Type1;
typedef B Type2;
};
typedef HelloBase<int, float> Hello;
template <typename C>
class Check
{
typedef typename C::Type1 A;
typedef typename C::Type2 B;
void Foo(A,B,C)
{
// A is int .. B is float .. C is Hello
}
};
...
Check<Hello> a;
So TemplatedClass doesn't need to change, and you can put everything you were going to put in Hello into HelloBase (using the templating as a tool for simply carrying around types).
What you want is impossible in the general case- what if Hello derived from TemplatedClass twice?
It is, however, fairly simple to do, even non-intrusively, under C++0x.
template<typename A, typename B> struct retval {
typedef A first;
typedef B second;
};
template<typename one, typename two> one first(const TemplatedClass<one, two>& ref);
template<typename one, typename two> two second(const TemplatedClass<one, two>& ref);
template<typename T> class Check {
typedef decltype(first(*static_cast<T*>(nullptr))) first;
typedef decltype(second(*static_cast<T*>(nullptr))) second;
};
In C++03 then you can still get the parameters inside the method, but can't access them outside, unless you insert special typedefs.