Template member function with stl - c++

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

Related

Template Class Specialization: Additional Members

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.

Template and inheritance

If I have a base class and its hierarchy :
class BaseClass {
}
class DerivedClass : public BaseClass {
}
And a templated class :
template <class T> struct TemplatedClass {
}
How can I achieve this ? :
// 1:
void doSomething(TemplatedClass<BaseClass *> const &t);
// 2:
std::vector<TemplatedClass<BaseClass *>> v;
TemplatedClass<DerivedClass *> a;
// Doesn't compile
doSomething(a);
// Doesn't compile
v.push_back(a);
You could have your templated classes have a hierarchy, too. You'll need to specify the bases, however:
template <typename...>
struct TC;
template <>
struct TC<> {
virtual ~TC() {}
};
template <typename T, typename... B>
struct TC
: TC<B...> {
// ...
};
With the variadic argument for the inheritance this should allow you specifying the relationship between the templates to mimick the inheritance hierarchy of the underlying. For example:
TC<Base>* d = new TC<Derived, Base>(/*...*/);
You need to make TemplatedClass<DerivedClass*> convertable to TemplatedClass<BaseClass *>, as they are independent types. Best way would be to have constructor, something like this:
template <class T> struct TemplatedClass {
template<class P> TemplatedClass( const TemplatedClass<P> &an );
};
Looks like you are trying to make something like smart pointer, you should look how it is already done on std::shared_ptr or boost::shared_ptr.
If you plan to use this code in production and/or share it with your team you may want to put additional checks on data type passed to this ctor:
template <class T> struct TemplatedClass {
template<class P, class = typename std::enable_if<std::is_convertible<P, T>{}>::type>
TemplatedClass( const TemplatedClass<P> &an );
};
Again best way would be to look into sources of shared_ptr and see how it is done there.

Howto use a c++ template typedef in a template class

I'd like to typedef a template type and use this type in a second template class.
First I defined the typedef with a helper struct
template<class T>
struct MyList {
typedef std::map<int, T> Type;
};
and then used it in the second template:
template <class T>
class MySecondClass {
public:
MySecondClass(MyList<T>& list) : list_(list) {}
private:
MyList<T>::Type list_;
};
Unfortunately, the use of MyListT& list; doesn't work and creates an error.
I think this is what you want:
template <typename T>
class MySecondClass {
public:
MySecondClass(typename MyList<T>::Type& list) : list_(list) {}
private:
typename MyList<T>::Type& list_;
};
MyList<T>::Type is not the same type as MyList<T> and you are mixing them in MySecondClass. Also you may need a typename prefix on that last declaration.

Templated deriving from a template

Given two templated classes which are very similar but behave differently:
template<class T>
firstBase {};
template<class T>
secondBase {};
Now I've got this other class, which, based on it's template parameter, will derive either from firstBase or from secondBase:
template<class B, class T>
myClass : public B<T> { /* T is used in here */ };
Well, that does not work. The compiler tells me that B is an unknown template name. (error: unknown template name 'B')
My current workaround is to define myClass as
template<class B, class T>
myClass : public B { /* T is used in here */ };
and the caller of myClass needs to intatiate it via myClass<b<t>, t> instead of myClass<b, t>.
The latter would be really nice and reduce some copy&paste code. Is there any other way of achieving this?
In my use case I'm trying to implement a deep_const_ptr for the pimpl idiom enabling 'true constness'. Depending on whether myClass needs to be copy-assignable or not, it either uses deep_const_ptr<std::shared_ptr> or deep_const_ptr<std::unique_ptr> for its private pointer.
#include <memory>
#include <iostream>
template<class pointerT, class typeT>
class deep_const_ptr : public pointerT
{
public:
explicit deep_const_ptr(typeT* ptr) : pointerT(ptr) { }
// overloading pointerT::operator->() for non-constant access
typeT* operator->() {
std::cout << "deep_const_ptr::operator->()" << std::endl;
return pointerT::operator->();
}
// overloading pointerT::operator->() for constant access
const typeT* operator->() const {
std::cout << "deep_const_ptr::operator->() const" << std::endl;
return pointerT::operator->();
}
};
Edit
So I ended up, as suggested by Luc Danton in his answer, passing std::unique_ptr<myClass::Private> or std::shared_ptr<myClass::Private> to my custom deep_const_ptr:
template<typename pointerTypeT>
class deep_const_ptr : public pointerTypeT {
explicit deep_const_ptr(typename pointerTypeT::element_type* ptr) : pointerTypeT(ptr);
typename pointerTypeT::element_type* operator->();
const typename pointerTypeT::element_type* operator->() const;
};
deep_const_ptr<std::unique_ptr<Test::Private>> d_unique;
deep_const_ptr<std::shared_ptr<Test::Private>> d_shared;
What you want is template template parameter:
// vvvvvvvvvvvvvvvvvv
template<template<typename> class B, class T>
class myClass : public B<T> { /* T is used in here */ };
Then, use that like:
myClass<firstBase,int> myIntClassObject;
myClass<secondBase,bool> myBoolClassObject;
For std::unique_ptr, you can make a wrapper:
template<typename T>
class uniquePtr : public std::unique_ptr<T>
{
};
or
template<typename T>
using uniquePtr = std::unique_ptr<T>;
I recommend against template template parameters in general. For instance std::unique_ptr and std::shared_ptr are different in that the former accepts two type parameters and the latter just one. So if you declare e.g. template<template<typename> class B, typename T> class foo; then foo<std::shared_ptr, int> is valid but foo<std::unique_ptr, int> isn't.
In your particular case you could use template<typename...> class B as a parameter, because that kind of template template parameters is special. Still, this would only accept templates that only take type parameters (not even template template parameters). Sometimes this can be worked around with alias templates, sometimes it can't.
In my experience there are better alternatives -- for instance you can conditionally inherit:
template<typename T>
struct pointer: std::conditional</* Make a choice here*/, std::unique_ptr<T>, std::shared_ptr<T>>::type {};
Or why not just accept the smart pointer as the parameter itself:
template<typename Pointer>
struct foo {
Pointer pointer;
/* typename Pointer::element_type plays the role that T used to have */
};
The Standard Library itself takes some steps to avoid template template parameters: have you ever noticed that an std::vector<T> uses std::allocator<T> as an argument, and not std::allocator? As a tradeoff, it means that an Allocator must provide a rebind member alias template.
B should be a template template parameter:
template<template <class> class B, class T>
class myClass : public B<T> { /* T is used in here */ };
Now you can give either firstBase or secondBase as the first template argument because they are templates.
You simply need to use a template-template parameter to get it to work:
template <
template <class> class B, class T>
// ^^^^^^^^^^^^^^^^
class myClass : public B<T> { / ... / };
It seems what you need is template template parameters:
template<template<class> class B, class T>
// ^^^^^^^^^^^^^^^
class myClass : public B<T> { /* T is used in here */ };
Now the first template argument for the myClass class template must be itself a class template that accepts one template (type) parameter. So, putting everything together:
template<class T>
class firstBase {};
template<class T>
class secondBase {};
template<template<class> class B, class T>
class myClass : public B<T> { /* T is used in here */ };
And here is how you would instantiate your myclass template to create a class that derives from firstBase<int>:
myClass<firstBase, int> obj;
Finally, here is a live example.

How can you define a specialization of a method of a templated class nested in a templated class outside of the class declaration?

The following gives me a couple compile errors:
error C2995: 'void A<T>::B<Q>::func(void)' : function template has already been defined
error C3855: 'A<T>::B<Q>': template parameter 'Q' is incompatible with the declaration
How can I do this without having the definitions in the class declaration?
template<typename T>
struct A
{
template<typename Q>
struct B
{
void func();
};
};
template<typename T>
template<typename Q>
void A<T>::B<Q>::func()
{
}
template<typename T>
template<>
void A<T>::B<int>::func()
{
}
Edit:
According to 14.7.3 ยง16 a nested class template cannot be specialized if it's enclosing class template is not also specialized. However, that makes me wonder why the nested class specialization works when it's completely defined within the outer class declaration like so:
template<typename T>
struct A
{
template<typename Q>
struct B
{
void func(){}
};
template<>
struct B<int>
{
void func(){}
};
};
Perhaps this is just VS2010 allowing me to do something I shouldn't be able to do?
The problem is in the fact that you need to be able to declare the templated type when you use the class (or struct).
So, if you have a templated nested class, its type would need to be set in the class itself, because you won't be able to do that from the "outside".
for example:
template<typename T>
struct A
{
template<typename Q>
struct B
{
void func(){}
};
};
Now, lets say you'd want to declare a variable of type A<int> with B<int> ... how would you do it ?
A<int>::B<int> a; //this syntactically would mean a is of type B<int>
A<int,int> a; //this would mean A is a templated class with 2 templated types
So, you can't really access B in the declaration.
So, B's type has to be set within the class A.
Nested Class Templates