I am trying to create a template class. But based on its data type, it should have an additional member variable. I've been trying different methods but could not find a solution.
Basically, the class will have 2 member variables: a and b. But if DataType is not void, it should have an extra member called data.
How can I achieve this?
#include <type_traits>
template <typename DataType>
class MyClass {
public:
int a;
char b;
};
template <typename DataType, typename std::enable_if<false == std::is_same<DataType, void>::value>::type>
class MyClass {
DataType data;
};
But if DataType is not void, it should have an extra member called data. - so void seems to be a specialization.
template <typename DataType>
class MyClass {
public:
int a;
char b;
DataType data;
};
template <>
class MyClass<void> {
public:
int a;
char b;
};
If I understood correctly, there is a way to "extend" your class members, if you "don't want to repeat yourself":
template <typename DataType>
class MyClass;
template <>
class MyClass<void> {
public:
int a;
char b;
};
template <typename DataType>
class MyClass : public MyClass<void> {
public:
DataType data;
};
But void is still not your primary template, but a specialization. Since, different specializations are completelly different types in the end, you can publically inherit a specialization in your primary class template definition, thus extending those members.
Related
I have the following existing code snippet in MyQueue.h
class MyQueue {
struct A {
explicit A(ValType init){
}
ValType memberA;
};
struct B {
explicit B {...}
std::list<A> listofA;
};
std::unordered_map<std::string, B> myMap;
};
ValType is a know class type defined in a different file.
GOAL
I need to templatize Valtype, meaning struct A constructor (or anywhere Valtype is used) can be Valtype or Valtype2.
My Questions are as follows
Does the whole class MyQueue need to be a template class or can the relevant member variables and member function be templatized like I have below. What are the general rules to decide this?
Can stl container be of template type? ex std::unordered_map<std::string, B<T>> myMap; std::list<A<T>> listofA;
This class has a cpp file with definitions of class member functions that use A, B and myMap.
class MyQueue {
template <class T>
struct A {
explicit A(T init){
}
T memberA;
};
struct B {
explicit B {...}
template <class T>
std::list<A<T>> listofA;
};
template <class T>
std::unordered_map<std::string, B<T>> myMap;
};
I made the class a template class
template<class TypeName>
class MyQueue {
};
For defining the member functions, I moved definitions from MyQueue.cpp to newly created MyQueue-inl.h which I include at the end of MyQueue.h
There is following c++ raw code:
template<typename T>
class A {
private:
// here I want to access B::SomeStruct to create SomeStruct field in class A
};
template<typename T>
class B {
private:
template<typename Tp>
friend class A;
struct SomeStruct {
void some_field;
};
};
In class A I want to create a field with the type of SomeStruct - struct declared in class B as a private member. Does it even possible?
Restrictions:
Forbidden to create a global struct, accessible both A and B classes.
Forbidden to create any public fields in class B.
This would be one way. All B:s befriends all A:s and A<T> has a B<T>::SomeStruct member:
template<typename T>
class B {
private:
template<typename Tp>
friend class A;
struct SomeStruct {};
};
template<typename T>
class A {
private:
// 'typename' prior to dependent type name 'B<T>::SomeStruct':
typename B<T>::SomeStruct ss;
};
Consider this case where:
#include<iostream>
template<class U>
class Table_Inside {
U a;
};
template<class T>
class Table {
Table_Inside<T> *tmp;
};
int main() {
Table<int> obj;
}
Doing this will create a data member of type int for Table_Inside class/object. I am struggling to think that what would happen if I remove templates for Table_Inside class and create two separate classes like Table_Inside_int and Table_inside_char. Assuming that only two options can come for U. In this case, how should we deal so that Table class has pointer to corresponding class. E.g.
#include<iostream>
class Table_Inside_int {
int a;
};
class Table_Inside_char {
char c;
}
template<class T>
class Table {
/*<what to write here to create corresponding class pointer>*/ *tmp;
}
int main() {
Table<int> obj;
}
So, if I pass,
Table<int> obj
It should create an pointer(tmp) of type Table_Inside_int inside Table class.
AND if I pass,
Table<char> obj
It should create an pointer(tmp) of type Table_Inside_char inside Table class.
Is it even possible in c++ world?
If specialization doesn't quite work and there's only a couple types, an alias can be made using std::conditional and std::is_same:
// assumes only char or int will be passed
template <class T>
using Table_Inside = std::conditional_t<std::is_same_v<T, char>, Table_Inside_char, Table_Inside_int>;
This can be used just as before like Table_Inside<T>.
This can easily be done through specialization:
// The "base" generic case
template<typename T>
class Table_Inside;
template<>
class Table_Inside<char>
{
// Special implementation for characters
};
template<>
class Table_Inside<int>
{
// Special implementation for integers
};
template<typename T>
class Table
{
Table_Inside<T>* tmp;
};
If you need common functionality shared between the char and int classes, then you can use inheritance for that.
You can handle this with some trait. Define
// Primary template
template <class T>
struct Table_Trait;
// Specialization
template <>
struct Table_Trait<int> { using type = Table_Inside_int; };
template <>
struct Table_Trait<char> { using type = Table_Inside_char; };
template <class T>
using Table_Inside_t = typename Table_Trait<T>::type;
and use it via
template<class T>
class Table {
Table_Inside_t *tmp;
};
However, there might be a simpler solution if there is no need to have independent classes Table_Inside_int and Table_Inside_double. You can use specialization also directly
// Primary template
template <class T>
class Table_Inside;
// Specialization
template <>
class Table_Inside<int> { /* implementation for int */ };
template <>
class Table_Inside<char> { /* implementation for char */ };
I have the following template class :
template <typename T>
class myClass
{
public:
// Many methods...
protected:
private:
T attribute
// Other attributes.
};
Instantiating an object of type myClass<void> does not work, because of void attribute.
Can you give me some hints to be able to use objects of type myClass<void> without specializing the whole class. Since it has many member functions that rely on the type T, specializing it will lead to code duplication.
Create a templated base class containing attribute, specialize it for void and inherit from it:
namespace detail //Warn end user that he should not use stuff from here
{
template <typename T>
struct myClass_base
{
T attribute;
};
template <>
struct myClass_base<void>
{}; //No attribute at all
}
template <typename T>
class myClass: private detail::myClass_base<T>
{
//rest of definition
};
This would make myClass lack attribute field when instantiating it with type void
You can defer the whole problem by using a custom type and specializing that:
template<typename T>
struct my_type_t
{
using type = T;
};
template<>
struct my_type_t<void>
{};
template<typename T>
using my_type = typename my_type_t<T>::type;
template <typename T>
class myClass
{
public:
// Many methods...
protected:
private:
my_type<T> attribute
// Other attributes.
};
Then at least you don't have to duplicate the whole rest of the class again.
But it probably does not make that much sense, as you surely want to use the type somewhere. So you would have to specialize that places further.
assume that I have the following two template classes :
template <class _A>
class First
{
private:
int a;
};
template <class _B>
class Second
{
private:
int b;
};
how can I link them in many-to-many friendship. for example, adding a method in First that prints b of a parameter object of Second.
is my question clear?
template <typename T>
class First {
int a;
template<typename> friend class Second;
};
template <typename T>
class Second
{
int b;
template<typename> friend class First;
};
This will enable every First<T> to access the internals of every Second<U>. Now, while this is the technical solution, you might want to consider whether a design with cyclic dependencies and opening up the internal to any instantiation of the other class is the best solution to your particular problem.
BTW, if you only want to grant First<int> access to Second<int> (and not Second<double>) you can do that like so:
template <typename> class Second;
template <typename T>
class First {
int a;
friend class Second<T>; // only befriend the same instantiation
};
template <typename T>
class Second {
int b;
friend class First<T>;
};
In this second version you need the forward declaration of the Second template before befriending a particular instantiation, but this allows you to grant access to the internals of the class only to a particular instantiation.
Assuming you understand protection, is the issue forward declaration of templates:
#include <iostream>
template <class _B> class Second; // Forward declare
template <class _A>
class First
{
public:
template<class _B>
void print(const Second<_B>& b)
{
std::cout << b.b << std::endl;
}
int a;
};
template <class _B>
class Second
{
public:
int b;
};
void testIt()
{
Second<double> sd;
First<int >fi;
fi.print<double>(sd);
}
You could start off with a declaration of each class:
template< typename T > class First;
template< typename T > class Second;
And now both classes will know about the other one in their definitions. You can declare them as friends there if you need to.
template< typename T > class First
{
template< typename U> friend class Second;
};
and the same in reverse.
You can also implement function bodies under the class definitions if they need to see each other's details, i.e. they cannot use a "forward declaration" copy.