Remove redundant template types - c++

I am currently struggling with templates: I have a templated class A, which performs basic math (for floats, doubles, complex numbers) and looks like this
template <typename T>
class A
{
public:
void foo(std::vector<std::complex<T>>& result);
};
Now I can use the class like A<double>, A<float>, but I would also like to use it like A<std::complex<float>> and A<std::complex<double>>. When using the latter, I would like the definition of foo to look like
void foo(std::vector<std::complex<float>>& result);
and not like
void foo(std::vector<std::complex<std::complex<float>>>& result);
Is there any way to create a specific template for the std::complex<T> cases, in which I can access the "inner" type? Or this is not possible/bad practice?
What is the most elegant way to solve this issue?

Another way can pass through the creation of a type traits to detect the (extract, when needed) the float type
template <typename T>
struct getFloatType
{ using type = T; };
template <typename T>
struct getFloatType<std::complex<T>>
{ using type = T; };
and use it in A (see fT)
template <typename T>
class A
{
public:
using fT = typename getFloatType<T>::type;
void foo(std::vector<std::complex<fT>>& result)
{ }
};

You can make a partial specialization for any instantiation of std::complex, e.g.
template <typename T>
class A<std::complex<T>>
{
public:
void foo(std::vector<std::complex<T>>& result);
};
Then for A<std::complex<double>>, the signature of foo would be void foo(std::vector<std::complex<double>>& result);.
To handle those duplicated codes, you can make a base class and move the common members into it, and make the primary template and partial specialization both derive from it. e.g.
class Base {
public:
void bar(...);
};
then
template <typename T>
class A : public Base {
...
};
template <typename T>
class A<std::complex<T>> : public Base {
...
};

Related

Type of class based on template type

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 */ };

Is it possible to make a template specialization to be equal to another type

If I have e.g.:
template <class T>
class MyOptional { /*...*/ };
I know that I can define a specialization e.g. for T = bool which will have a different (separate) implementation:
template <> class MyOptional<bool> { /*...*/ };
But is it possible to say that this T=bool specialization equals to another type (e.g. MyOptBool)?
Something like this:
class MyOptBool { /*...*/ };
using Optional<bool> = MyOptBool; /* impossible but looking for similar functionality */
The only think I have found is using inheritance:
class MyOptBool { /*...*/ };
template <> class MyOptional<bool> : public MyOptBool {
using MyOptBool::MyOptBool;
};
1st question: Is there any more elegant solution? (just for curiosity + I have some conversion issues, etc.).
2nd question: Is the "workaround" using inheritance for declaring two classes equal commonly used (in some important libraries, etc.)?
One possible approach that allows Optional<T> to resolve in this fashion, even though it's not actually ending up specializing MyOptional, per se:
template<class T>
class MyOptional {};
class MyOptBool {};
template<class T>
struct OptionAlias {
typedef MyOptional<T> type;
};
template<>
struct OptionAlias<bool> {
typedef MyOptBool type;
};
template<typename T>
using Optional = typename OptionAlias<T>::type;
void foo()
{
Optional<int> x;
Optional<bool> y;
MyOptBool z=y;
}

Using template class with the incomplete type void

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.

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.

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.