C++: Way around dependent templates - c++

I have a class template like this:
template <typename T1, typename T2>
class ClassName {
// Members variables and Functions based on the template parameter types
};
(T1, T2) can either be (class A, class B) or (class C, class D) only.
So, T1 alone is enough to determine T2.
Is there any way to only take T1 as a parameter and write the same class? If yes, how?

If your template only depends on a single parameter, you should not declare it with 2 parameters. You can use std::conditional together with std::is_same to declare conditional aliases.
If you want:
(T1, T2) can either be (class A, class B) or (class C, class D) only
If "only" is meant as: User only ever instantiates for A or C and in that case T2 should be B / D:
#include <type_traits>
struct A {};
struct B {};
struct D {};
template <typename T>
struct Foo {
using T1 = T;
using T2 = std::conditional_t<std::is_same_v<A, T>, B, D>;
};
Foo<A>::T2 is B and Foo<C>::T2 is D. Actually Foo<T>::T2 is D for any T that is not A. In other words, as mentioned above, the premise is that it is only instantiated for either A or C. If the template itself should make sure that only instantiations for A or C are valid, you might want to add a static_assert (again using std::is_same to check that T is among the allowed types).

Make an enumeration of your configurations:
enum class ClassConfigEnum { AB, CD};
template<ClassConfigEnum config> struct ClassConfig;
template<> struct ClassConfig<AB> {
using T1 = A;
using T2 = B;
};
template<> struct ClassConfig<CD> {
using T1 = C;
using T2 = D;
};
And then have the class use the enumerated configuration
template <ClassConfigEnum config>
class ClassName {
using T1 = ClassConfig<config>::typename T1;
using T2 = ClassConfig<config>::typename T2;
};

You can write a type trait:
#include <iostream>
#include <type_traits>
struct A {};
struct B {};
struct C {};
struct D {};
template <typename Type> struct Dependent {};
template<>
struct Dependent<A> {
using type = B;
};
template<>
struct Dependent<C> {
using type = D;
};
template <typename Type>
class ClassName {
using Dependent = typename Dependent<Type>::type;
};
int main()
{
static_assert(std::is_same<Dependent<A>::type, B>::value, "");
static_assert(std::is_same<Dependent<C>::type, D>::value, "");
// Dependent<B>::type a; error: 'type' is not a member of 'Dependent<B>'
}

Related

Shortcuts for repeated template arguments

Let's say I have a class:
template <typename A, typename B, typename C>
class Foo {
};
in which usually A and B are the same type, but I'd like to keep the option to define B separately. I'd like to define an additional template, say,
template <typename A, typename C>
class Foo {
};
that is just a wrapper for Foo<A, A, C>. Is this common practice? If so, what's the simplest way to do it?
Not the only solution, but if it is viable it is the simplest: Change order of B and C and provide a default for B:
#include <type_traits>
template <typename A, typename C, typename B = A>
class Foo {
};
int main(){
using Foo1 = Foo<int,double,int>;
using Foo2 = Foo<int,double>;
static_assert( std::is_same<Foo1, Foo2>::value);
}
I understand your question this way (based on comment, question is composed with two problems, both get answered seperetly): If A and B represent same type use alternative version of template.
So looks like actually you are trying to do is partial template specialization.
Example:
template <typename A, typename B, typename C>
class Foo {
public:
using type = B;
};
template <typename A, typename C>
class Foo<A, A, C> {
public:
using type = C;
};
simple demo: https://godbolt.org/z/KPz4jj

Nested template using/typdef

Say I have this setup:
template<typename T1>
struct A {
template<typename T2>
struct B {
using type = int;
};
};
I'd like to be able to form a typdef/using:
template<typename T1,typename T2>
using type2 = A<T1>::B<T2>::type;
//... and use like
type2<int,char> foo;
GCC complains that I need typename A<T1>::B<T2>::type instead, and afterwards complains that it expects ";" before "<" after B (i.e. typename A<T1>::B)
is there no way to use "using" with nested templates?
Switch from
using type2 = A<T1>::B<T2>::type;
to
using type2 = typename A<T1>::template B<T2>::type;
Note that B is a templated class and type is enclosed in a templated class, hence use the following
#include <iostream>
template<typename T1>
struct A {
template<typename T2>
struct B {
using type = int;
};
};
template<typename T1,typename T2>
using type2 = typename A<T1>::template B<T2>::type;
int main()
{
type2<int,char> foo =2;
std::cout << foo;
}

How to extract the types passed in template parameters?

In the below code, I want to replace "something" with something that will allow me to declare the type of 'f' as the second parameter passed in C (while passing C to M's template), i.e., float here.
#include<iostream>
using namespace std;
template<class A, class B>
class C{
public :
A a;
B b;
};
template<class D>
class M{
public :
decltype(D::<something>) f; //!!!!!!!!!!!!!!!!
void show(){cout<<f;}
};
int main(){
M<C<int,float>> m;
m.show();
}
You can do this with some trickery.
template<typename> class M; // leave undefined
template<template<typename, typename> D,
typename One,
typename Two>
class M<D<One, Two>> { // a specialisation
Two f;
};
Now you can pass to M a class that has exactly two template parameters (sich as C<int, float>). If you pass something else (e.g. int) there will be an error.
What about a template taking a template?
With the following line you can declare a template taking a template and have the latter's template arguments types named:
template<
template<typename, typename> class D,
typename A, typename B>
With this notation you are saying that D is a is a template parameter which is itself in turn a template and A and B are the types that it takes.
Now you can refer to the first and second template parameter of D with the names A and B.
using namespace std;
template<class A, class B>
class C{
public :
A a;
B b;
};
template<template<typename, typename> class T, typename A, typename B>
class M{
public :
B f; //!!!!!!!!!!!!!!!!
void show(){cout<<f;}
};
int main(){
M<C, int, float> m;
m.show();
}
For more info check this answer out.

How to get struct in templated class to take any type of template parameters?

I got a class A template<typename T1, typename T2> class A{...};
Within this class I would like to have a struct B {
(type of any of the parameters of A) m_type;
}. So that I could have a B with m_tpye of T1 and a B with m_type of T2. Is this possible?
You can use a union:
template<typename T1, typename T2> class A {
struct B {
union {
T1 t1;
T2 t2;
} m_type;
};
};
However, unions have strict restrictions that might not be acceptable in your case, and they are easy to misuse. I recommend using a type-safe variant type instead. Unfortunately there is no such type in the standard library yet, so you need to use a third party implementation, or implement it yourself, or try to get the proposal N4542 into the standard.
I also recommend considering whether you actually need to have objects of type B that have members of different type.
Maybe something like the example below can do the job:
template<typename... T>
struct B: T... { };
template<typename T1, typename T2>
struct A{
B<T1,T2> b;
};
struct S1 { };
struct S2 { };
void f1(const S1&) { }
void f2(const S2&) { }
int main() {
A<S1, S2> a;
f1(a.b);
f2(a.b);
}
Here B inherits from both S1 and S2, so actually it is both of them, as requested by the OP.

Linking classes with metafunctions

Consider
class B; class C;
class A {
using linked_from = std::tuple<B,C>; // i.e. A is "linked from" B and C.
};
class B {
using linked_to = std::tuple<A,C>; // i.e. B is "linked to" A and C.
};
class C {
using linked_to = std::tuple<A>;
using linked_from = std::tuple<B>;
};
What I want to do is redesign the above so that there is no maintenance responsibilities whenever the linked_to member types are changed. Perhaps something along the lines of
template <typename To, typename From>
struct LinkFromMap {
using linked_from = From;
};
template <typename From, typename To>
struct LinkPair : LinkFromMap<To, From> {};
template <typename T, typename... Ts>
struct Link : LinkPair<Ts, T>... {
using linked_to = std::tuple<Ts...>;
};
class B; class C;
class A {};
class B : public Link<B, A,C> {};
class C : public Link<C, A> {};
int main() {
static_assert(std::is_same<B::linked_to, std::tuple<A,C>>::value, "");
// Still need to obtain std::tuple<B,C> from A, std::tuple<B> from C, etc...
}
A and C do not need to keep their linked_from member types, as long as std::tuple<B,C> can be obtained from A and std::tuple<B> can be obtained from C. That is why I've defined LinkFromMap above, but I can't figure out how to use LinkFromMap to achieve that. Can it be used at the end somehow to form the packs <A, B,C> and <C,B> or something like that? Or is there a better way to automate type retrievals that linked_from was designed for?