I am just working on my school homework, and I am interested if C++ could create specialized destructor for pointers. I know that this is not good practice, but because of performance, I want to do it this way. Also because I am curious about it. So lets say, that we have this class:
template<typename T,int(*comp)(const T& first, const T& second)>
class BinomialHeapLite
{
private:
class Node
{
Node* next;
T instance;
}
Node* top;
public:
~BinomialHeapLite();
}
Now I want destructor, that delete just node, if is T only type and delete also inner instance, if is T pointer..
template<typename T, int(*comp)(const T& first, const T& second)>
BinomialHeapLite<T,comp>::~BinomialHeapLite()
{
//some code
delete this->top;
}
template<typename T, int(*comp)(const T* &first, const T* &second)>
BinomialHeapLite<T*,comp>::~BinomialHeapLite()
{
//some code
delete this->top->instance;
delete this->top;
}
But this give me "invalid use of incomplete type". I also want to use pure C++11, because I want to be independent to another libraries (also standards libraries), moreover that libraries are not permitted in out system.
Is something like this possible in pure C++?
You're giving your BinomialHeapLite class a responsibility that it shouldn't have: cleaning up instance if it's a heap-allocated pointer.
That burden should be on the user of your class: what if he is already calling delete in its own code? What if the object is supposed to be reused after your BinomialHeapLite is destroyed?
All your class should do is provide a generic container that manages its own memory. For that task, your should also use smart pointers (std::unique_ptr in this case):
template<typename T,int(*comp)(const T& first, const T& second)>
class BinomialHeapLite
{
private:
class Node
{
std::unique_ptr<Node> next;
T instance;
}
std::unique_ptr<Node> top;
}
You won't need an hand-written destructor then. Refer to the "rule of three/five/zero" for more information.
If you really want to implement your flawed/unconventional design, you could use a partially specialized helper struct that calls delete if its type is a pointer:
template <typename T>
struct dtor_helper
{
static void do_it(T&&){}
};
template <typename T>
struct dtor_helper<T*>
{
static void do_it(T* x){ delete x; }
};
template<typename T, int(*comp)(const T& first, const T& second)>
BinomialHeapLite<T,comp>::~BinomialHeapLite()
{
//some code
dtor_helper<T>::do_it(this->top->instance);
delete this->top;
}
wandbox example
You can not partially specialize a member function. But you can partially specialize a class template:
template <class T>
struct Deallocator{
template <class Node>
static void Deallocate(const Node &node){}
};
template <class T>
struct Deallocator<T *>{
template <class Node>
static void Deallocate(const Node &node){}
};
template<typename T, int(*comp)(const T& first, const T& second)>
BinomialHeapLite<T,comp>::~BinomialHeapLite()
{
//some code
Deallocator<T>::Deallocate(top);
}
Or overload a function :
template<typename T,int(*comp)(const T& first, const T& second)>
class BinomialHeapLite
{
private:
struct Node // a little change here
{
Node* next;
T instance;
};
Node* top;
template <class Ty>
void deallocate(Ty *) {}
template <class Ty>
void deallocate(Ty &) {}
public:
~BinomialHeapLite();
};
template<typename T, int(*comp)(const T& first, const T& second)>
BinomialHeapLite<T,comp>::~BinomialHeapLite()
{
//some code
deallocate(top->instance);
}
Related
I am doing a project using generic templates in C++ in modular programming (we don't use OOP concepts)
We are having an issue understanding how to refer in an auxiliary function to a single variable in the template, which is a struct itself, without referring to the whole struct.
To put an example, because it sounds weird, we have:
template<typename T>
struct tree {
friend void addElement<T> (tree<T>& c, const T& e);
struct Node {
T element; // template element
Nodo* left;
Nodo* right;
};
Node* root;
int size;
}
template<typename T>
void friend void addElement<T> (tree<T>& c, const T& e);
insert(c.root, e);
c.size++;
}
// auxiliary function
template<typename T>
void insert(tree<T>root node, const T& e) {
// how to refer to taking a Node* as an argument? we want
// to modify the node structure of the tree in a recursive way,
// so we will need to pass Node->left or Node->right as arguments
// code
}
We have tried multiple ways of doing this, none worked so far. How could this be done?
Thanks!
Syntax would be tree<T>::Node* node and as it is a dependent name, we have to use extra typename: typename tree<T>::Node* node:
template<typename T>
void insert(typename tree<T>::Node* node, const T& e) {
// ...
}
template<typename T>
void addElement(tree<T>& c, const T& e)
{
insert(c.root, e);
c.size++;
}
Demo.
You probably want to pass that pointer by reference though.
I need a 'MultiStack' taking different types of objects, putting each type in a separate stack.
This is what it looks like so far. The open problem is: how to handle the containers for a number of different T
class MultiStack
{
public:
template<typename T>
const T& Get()
{
return Container<T>.back();
}
template<typename T>
void Push( const T& t )
{
Container<T>.push_back( t );
}
template<typename T>
void Pop( const T& /*t*/ )
{
Container<T>.pop_back();
}
private:
// this does not make sense, we obv. need one stack for each T
// template<typename T>
// std::vector<T> Container;
};
Now, I could use the old trick, putting the Container in a member function, like
template<typename T>
auto GetContainer()
{
static std::vector<T> C;
return C;
}
but I don't like this anymore in the age of multi-threading. It is 'dangerous', right!?
Is there a better, elegant way? It is conceivable that I know the allowed types beforehand, if that helps realizing it.
but I don't like this anymore in the age of multi-threading. It is 'dangerous', right!?
Issue is not multi-threading. initialization would be fine.
You still have to protect/synchronize access though, as regular multi-threading code.
Issue is that the container is not per instance of MultiTask, as it is static.
It is mostly as if MultiTask were a Singleton.
It is conceivable that I know the allowed types beforehand, if that helps realizing it.
That helps, you can then use std::tuple, something like (C++14):
template <typename ... Ts>
class MultiStack
{
public:
template<typename T>
const T& Get() const
{
return GetContainer<T>().back();
}
template<typename T>
void Push(const T& t)
{
GetContainer<T>().push_back(t);
}
template <typename T>
void Pop()
{
GetContainer<T>().pop_back();
}
private:
template <typename T>
const std::vector<T>& GetContainer() const { return std::get<std::vector<T>>(Containers); }
template <typename T>
std::vector<T>& GetContainer() { return std::get<std::vector<T>>(Containers); }
private:
std::tuple<std::vector<Ts>...> Containers;
};
I am having trouble using the first_ variable in my template class (shown below).
template<typename T> class enable_movable_ptr {
public:
//default constructor
enable_movable_ptr() : ptr_(nullptr) {};
enable_movable_ptr(T* p) : ptr_(p) {};
//...
//other constructors and operators
//...
T* get() {return ptr_; };
movable_ptr<T>* First() { return first_; };
private:
T* ptr_;
movable_ptr<T>* first_ = nullptr;
};
template<typename T> class movable_ptr {
public:
//Parameterless constructor
movable_ptr() : trg_(nullptr) {};
//Constructor from T*
movable_ptr(T* p) : trg_(p) { add_to_tracked(this); };
//...
//other constructors and operators
//...
//access to variables
enable_movable_ptr<T>* get() {return trg_; };
movable_ptr<T>* Next(enable_movable_ptr<T>& p) {return next_; };
movable_ptr<T>* Previous(enable_movable_ptr<T>& p) {return prev_; };
//get_movable
movable_ptr<T>* get_movable(enable_movable_ptr<T>& p) {};
private:
enable_movable_ptr<T>* trg_;
movable_ptr<T>* next_ = nullptr;
movable_ptr<T>* prev_ = nullptr;
};
template<typename T> movable_ptr<T> get_movable(enable_movable_ptr<T>& p){
if (p.First() != nullptr)
{}
};
The problem is, the returned first_ is of the <error-type> type (that's what it shows if I mouse over any first_ or First() in the code, except for the declaration in VS2019), instead of the expected movable_ptr<T>*. However, if I mouse over the declaration of first_, it shows the correct movable_ptr<T>* type.
From my point of view it seems as if the compiler has the memory of a goldfish, and forgets what the type is even while still within the same class definition. That is most likely not the case, and I suspect there is something I might have done wrong which causes this.
Any ideas on where I went wrong, or how to solve the problem?
Just forward declare movable_ptr before defining enable_movable_ptr.
template<typename T> class movable_ptr;
Otherwise, the compiler doesn't know that movable_ptr is even a thing that might exist.
Also, it's better to not rely on external tooling, such as your IDE, to tell you what is wrong with the code. Let the compiler do that; it very likely knows much more about your code.
Here's where the issue actually comes from (I've removed all the code that is irrelevant to your particular issue)
// you need to forward declare
template <typename T> struct movable_ptr;
template <typename T> struct enable_movable_ptr {
movable_ptr<T>* first_ = nullptr; // else compiler won't know what movable_ptr is
};
template <typename T> struct movable_ptr {
enable_movable_ptr<T>* trg_;
};
Here's a link to the mcve.
I'm new to std::shared_ptr and trying to use it in linked list implementation. The code is:
#pragma once
#include <memory>
template <typename T>
class Node
{
public:
Node();
Node(const T& anItem);
Node(const Node& anNode);
T getItem() const;
std::shared_ptr<Node<T>> getNext() const;
std::shared_ptr<Node<T>> getPrev() const;
void setItem(const T& anItem);
void setNext(std::shared_ptr<Node<T>> nextNodePtr);
void setPrev(std::shared_ptr<Node<T>> prevNodePtr);
private:
T item;
std::shared_ptr<Node<T>> next;
std::shared_ptr<Node<T>> prev;
};
template <typename T>
Node<T>::Node(): next(nullptr), prev(nullptr) {}
template <typename T>
Node<T>::Node(const T& anItem) : next(nullptr), prev(nullptr), item(anItem) {}
template <typename T>
Node<T>::Node(const Node& anNode) : next(anNode.getNext()), prev(anNode.getPrev()), item(anNode.getItem()) {}
template <typename T>
T Node<T>::getItem() const
{
return item;
}
template <typename T>
std::shared_ptr<Node<T>> Node<T>::getNext() const
{
return next;
}
template <typename T>
std::shared_ptr<Node<T>> Node<T>::getPrev() const
{
return prev;
}
template <typename T>
void Node<T>::setItem(const T& anItem)
{
item = anItem;
}
template <typename T>
void Node<T>::setNext(std::shared_ptr<Node<T>> nextNodePtr)
{
next = nextNodePtr;
}
template <typename T>
void Node<T>::setPrev(std::shared_ptr<Node<T>> prevNodePtr)
{
prev = prevNodePtr;
}
This is in a header file. In any of these member functions, dereferencing the shared pointer doesn't show up the members and methods.
For example, suppose that in setPrev() I typed the following:
prev->
Now if it's a raw pointer (I tested with raw pointer), intellisense will show the members and methods available. But for std::shared_ptr intellisense doesn't work, and the only thing I saw is the methods of std::shared_ptr itself (operator* for example). I'm wondering what I did wrong?
Additionally, if I type (*prev). VS2017 simply tells me:
IntelliSense: 'No members available'
BTW it works in main.cpp, just not working in the header file, which is painful as I have to check the header files frequently, so I'm wondering I did something wrong with the smart pointer.
How to reduce code duplication of a class that is template specialized?
I am trying to create a class (MyArray) that acts like std::vector but receives raw-pointer as parameter in some functions.
Here is a simplified version of it :-
template<class T> class MyArray{
T database[10];
public: T& get(int index){return database[index];}
void set(int index, T t){
database[index]=t;
}
};
template<class T> class MyArray<std::unique_ptr<T>>{
T* database[10];
public: T*& get(int index){return database[index];}
void set(int index, std::unique_ptr<T> t){
T* tmp=t.release();
database[index]=tmp;
}
};
Here is a test:-
class B{};
int main() {
MyArray<B> test1;
MyArray<B*> test2;
MyArray<std::unique_ptr<B>> test3;
test3.set(2,std::make_unique<B>()));
return 0;
}
Question: Please demonstrate an elegant way to reduce the above code duplication in MyArray.
A solution that I wished for may look like :-
template<class T> class MyArray{
using U = if(T=std::uniquePtr<X>){X*}else{T};
U database[10];
public: U& get(int index){return database[index];}
void set(int index, T t){
U u = convert(t);//<-- some special function
database[index]=u;
}
};
There might be some memory leak / corruption. For simplicity, please overlook it.
I just want an idea/rough guide. (no need to provide a full run-able code, but I don't mind)
In real life, there are 20+ function in MyArray and I wish to do the same refactoring for many classes.
Edit: I have (minor) edited some code and tag. Thank AndyG and Jarod42.
Maybe can you delegate the implementation details to a struct you provide to your class, and you specialize this struct, not MyArray:
template <typename T>
struct Policy {
using type = T;
static type convert(T t) { ... }
};
template <typename T>
struct Policy<std::unique_ptr<T>> {
using type = T*;
static type convert(T t) { ... }
};
template <typename T, typename P = Policy<T>>
class MyArray
{
using type = typename P::type;
void set(int index, T t) { type result = P::convert(t); }
};
You might think about using a common base class for the common functionality:
template<class T>
class Base{
protected:
T database[10];
public:
T& get(int index){return database[index];}
};
template<class T>
class MyArray : public Base<T>{
public:
void set(int index, T t){
this->database[index]=t;
}
};
template<class T>
class MyArray<std::unique_ptr<T>> : public Base<T*>
{
public:
void set(int index, std::unique_ptr<T>&& t){
T* tmp=t.release();
this->database[index]=tmp; //a little different
}
};
Demo