Templates can be used, to avoid virtual function calls.
#include <iostream>
struct Impl1{
void Run(){
std::cout << "Impl1" << std::endl;
}
};
struct Impl2{
void Run(){
std::cout << "Impl2" << std::endl;
}
};
template<typename T>
struct Runner{
T RunImplementation;
void MainLoop(){
RunImplementation.Run();
}
};
int main()
{
Runner<Impl1> loop1;
Runner<Impl2> loop2;
loop1.MainLoop();
loop2.MainLoop();
}
Types can be selected at compile-time, this can be useful if you want to implement some kind of adaptive templates. For example you can store big objects on the heap and small in the stack.
template<typename T>
using sharedPtrTypeSelector =
typename std::conditional<sizeof(T) <= sizeof(std::shared_ptr<T>), T, std::shared_ptr<T>>::type;
template<typename T>
class swStorage{
sharedPtrTypeSelector<T> data;
public:
bool operator==(const swStorage &rhs) const {
if constexpr (std::is_pointer_v<sharedPtrTypeSelector<T>>)
return *data == *rhs.data;
else
return data == rhs.data;
}
bool operator!=(const swStorage &rhs) const {
return !(rhs == *this);
}
operator T() const {
if constexpr (std::is_pointer_v<sharedPtrTypeSelector<T>>)
return *data;
else
return data;
}
swStorage(){
if constexpr (std::is_pointer_v<sharedPtrTypeSelector<T>>)
data = std::make_shared<T>(0);
}
explicit swStorage(const T& newData){
if constexpr (std::is_pointer_v<sharedPtrTypeSelector<T>>)
data = std::make_shared<T>(newData);
else
data = newData;
}
};
For example, I've used it to optimize storage in an Octree.
marknefedov / cubic-octree
Cubic octree, with on-demand node allocation.
Cubic-octree
Cubic octree with array-like access. Originally made to store voxel data for minecraft-like terrain.
Example
Octree<int, 5> octree; // This will create octree of size 32x32x32 with only root node allocated
octree.set(1, 2, 3, 4) // This will allocate nodes down to depth of 5 and set node at position [1,2,3] to value 4.
int val = octree.get(1, 2, 3) // Get value at node [1,2,3]
octree.SimplifyTree() // Collapse nodes with same values.
Top comments (0)