How to specialize a templated class at the class level - c++

Recently I was asked a question. I have the below templated class:
template<size_t SIZE>
class Cache
{
// A lot of class methods omitted
std::array<int, SIZE> _arr;
};
but someone might pass large a size and allocate on the stack, running out of stack memory. So you might suggest changing it to allocate on the heap:
template<size_t SIZE>
class Cache
{
// A lot of class methods omitted
std::unique_ptr<std::array<int, SIZE>> _arr;
};
but now those wanting a small array will pay the cost of indirection (i'm aware this is a very small cost but for the point of the question please accept).
Consequently it was hinted to me template specialization can allow some to choose the small std::array implementation and others to allocate their array on the heap.
I presume this specialization must be at the class level, because the std::array is a class member.
How is this class template specialization achieved without duplicating all the (omitted) class methods?

How is this class template specialization achieved without duplicating all the (omitted) class methods?
Abstract the thing you care about away into its own mixin class. For example:
template<size_t SIZE, bool> // default case, condition is false
class storage {
std::unique_ptr<std::array<int, SIZE>> _arr;
protected:
// constructor too...
std::array<int, SIZE>& arr() { return *_arr; }
};
template<size_t SIZE> // special case, condition is true
class storage<SIZE, true> {
std::array<int, SIZE> _arr;
protected:
// constructor too...
std::array<int, SIZE>& arr() { return _arr; }
};
Then simply have the cache use it as base, while checking SIZE for a threshold:
template<size_t SIZE>
class Cache : private storage<SIZE, (SIZE < THRESHOLD)>
{
// A lot of class methods omitted
// They now use this->arr();
};
You may also opt for composition instead. The specialization is pretty much the same in this case, but arr() is gonna need to be public so Cache may access it.

Related

How can I make RefCounting optional inside an allocator class trough template police class design?

My pool allocators have an inner class Block, that goes like this:
template<class T> PoolAlloc{
...
struct Block{
T userData;
RefCounting rc;
};
Block * m_pPool;
...
};
I dont know how to make the refcounting optional in an elegant way.
Passing an external Block as an extra template parameter is ugly, since I will have to give the T parameter to both the allocator and for the allocator parameter:
PoolAlloc<Foo, Block<Foo>>
Its redundant.
I tried specializing only the inner struct, but its my understanding this aint an option, inner classes require entire outter class specialization:
template<class T, bool refCounting> PoolAlloc{
...
struct Block;
...
};
template <class T, bool RC> struct PoolAlloc<T,RC>::Block{ T userData; RefCounting rc;};// default
template<class T> struct PoolAlloc<T, false>::Block{ T userData;}; // cant get this to compile
The answer is most likely easier than you think.
Imagine you have struct/class and you want to make something inside it optional (aka there or not there) this is the "policy" for want of a better word. The first thing you need to do is define 2 structs one with the real implementation and one with an empty implementation then depending on which you want you instantiate that. There is no other way of implementing this BTW so you will have to make do with some redundancy here. (Defaulted template parameters and template using can help reduce the redundancy a bit here.)
For example (not tested):
struct empty_ref_counter {
//empty reference counter implementation
void add_reference() {} // <-- no implementation!!
//other empty member functions
};
struct ref_counter {
//reference counter implmentation
unsigned ref;
void add_reference() {
++ref;
}
//other complete member functions
};
template<typename RefCountPolicy = empty_ref_counter>
struct strA {
typedef typename RefCountPolicy ref_policy;
RefCountPolicy count;
};
Note I did a typedef typename of ref_policy this is the reference policy used by strA and can be used to inspect the ref_policy by outside sources (aka functions that take a strA<RCP>).
In other words you can make Block a template struct, and still have access to T in the outer template class/struct.
struct BlockBase {};
template<typename RefCount>
struct Block : BlockBase {
T userData;
RefCount rc;
};
BlockBase* m_pPool;
In order to do this it may also be a good idea to give Block a non template base (possibly even with virtual destructor to ensure RefCount is properly destructed!!)

Should I have an Allocator as a member variable in my class?

If I want to make a class that uses allocators (like a custom string class), should I have the allocator instantiated as a member variable or not ?
// Should I do this ?
template <class Allocator>
class my_class {
Allocator a_;
void func_that_allocates() {
std::allocator_traits<Allocator>::allocate(a_, 10);
}
};
// Or this ?
template <class Allocator>
class my_class {
void func_that_allocates() {
std::allocator_traits<Allocator>::allocate(Allocator(), 10);
}
};
// Or maybe have a_ be a static member ?
My problem is that I'm trying to make a space-efficient string class that only stores the char pointer (to use it in a std::variant along with small primitive types), but having the allocator as a member doubles the size of my object even with an empty stateless allocator (like std::allocator).
Even if allocator is empty, it will still contribute to the object size. In C++20 we will have [[no_unique_address]] attribute to address this issue (now implemented in GCC 9 and Clang 9). You'll be able to write:
template<class Alloc>
class S {
int member;
[[no_unique_address]] Alloc allocator_;
};
For empty class Empty_allocator, sizeof(S<Empty_allocator>) will be equal to sizeof(int).
Without this attribute the standard trick is to use empty base class optimization, deriving your class from the allocator itself. This approach is used, e.g., in libstdc++'s implementation of std::vector:
template<typename Tp, typename Alloc>
struct Vector_base
{
typedef typename __gnu_cxx::__alloc_traits<Alloc>::template
rebind<Tp>::other Tp_alloc_type;
struct Vector_impl : public Tp_alloc_type
{ ... };
const Tp_alloc_type& M_get_Tp_allocator() const _GLIBCXX_NOEXCEPT
{ return *static_cast<const Tp_alloc_type*>(&this->M_impl); }
Alloc get_allocator() const _GLIBCXX_NOEXCEPT
{ return Alloc(M_get_Tp_allocator()); }
Vector_impl& M_impl;
};
If you going to use only default standard library allocator then you don't have to hold an allocator as a field in your class.
The std::allocator class template is the default Allocator used by all
standard library containers if no user-specified allocator is
provided. The default allocator is stateless, that is, all instances
of the given allocator are interchangeable, compare equal and can
deallocate memory allocated by any other instance of the same
allocator type.
When you are using a template - API user can specify some custom state-full allocator, in this case you will need a field.
As well as you always can use new[]/delete[] directly without any allocators. Or you can rewrite func_that_allocates in order to use stack memory only. i.e. replace: any std::vector with std::array any std::string with char tmp_str[128] = {'\0'}; etc.

sfinae away a destructor

I am implementing something very similar to std::vector but uses array on the stack instead of memory allocation.
The d-tor calls a function that uses SFINAE.
If value_type is POD the function have empty body.
If value_type is normal class such std::string, the function have a body and destroy all the data properly.
Now, I want to be able to use this new std::vector as constexpr. However even the c-tor is declared constexpr, the code does not compiles because the class have non trivial d-tor.
Here is small part of the code:
template<typename T, std::size_t SIZE>
class SmallVector{
constexpr SmallVector() = default;
~SmallVector(){
destructAll_<value_type>();
}
// ...
template<typename X>
typename std::enable_if<std::is_trivially_destructible<X>::value == true>::type
destructAll_() noexcept{
}
};
Is there anything I can do to make class be constexpr if value_type is POD and keeping functionality for non POD data types.
(Not at the same time of course)
until C+20
Unfortunately, there is no way to enable/disable destructor with SFINAE, nor with future concepts. That is because destructos:
can't be templated
can't have arguments
can't have a return type
What you can do is specialize whole class, or better yet, create a base class that contains only the construct/destruct and basic access and specialize that.
template <class T, class Enable = void>
struct X {
~X() {}
};
template <class T>
struct X<T, std::enable_if_t<std::is_pod<T>::value>> {
};
static_assert(std::is_trivially_destructible<X<int>>::value);
static_assert(!std::is_trivially_destructible<X<std::vector<int>>>::value);
C++ 20
As far as I can tell you can constraint a destructor and get exactly what you want in a very simple and elegant solution:
template<typename T, std::size_t SIZE>
class SmallVector{
public:
constexpr SmallVector() = default;
~SmallVector() requires std::is_trivially_destructible_v<T> = default;
~SmallVector()
{
}
};
static_assert(std::is_trivially_destructible_v<SmallVector<int, 4>>);
static_assert(!std::is_trivially_destructible_v<SmallVector<std::string, 4>>);
However this is a brand new feature and there have been some changes lately (e.g. see Disable non-templated methods with concepts) and the compiler support is still sketchy. gcc compiles this just fine, while clang is confused by the fact that there are two definitions of the destructor godbolt
The example of if constexpr in destructor. (C++17 required)
template<typename Tp, typename TLock>
struct LockedPtr {
private:
Tp *m_ptr;
TLock *m_lk;
void prelock(std::mutex *mtx) { mtx->lock(); }
void prelock(std::atomic_flag *atom) { while(atom->test_and_set(std::memory_order_acquire)); }
public:
LockedPtr(Tp *ptr, TLock *mtx)
: m_ptr(ptr), m_lk(mtx) {
prelock(mtx);
}
~LockedPtr() {
if constexpr (std::is_same_v<TLock, std::mutex>)
((std::mutex *)m_lk)->unlock();
if constexpr (std::is_same_v<TLock, std::atomic_flag>)
((std::atomic_flag *)m_lk)->clear(std::memory_order_release);
}
};
These code is the part of RAII locked smart pointer, to adopt to normal std::mutex and spinlock by std::atomic_flag.
Using function overload to match different type in constructor.
Match type by if constexpr and make something unconvertable to pointer in destructor.

C++ Template class methods that do not use template parameter

I have a class with M number of methods, and only one method performs read-only operations on a std::array. The rest of the M-1 methods do not use the std::array. Code below. Assume I can't break up this class.
My concerns are:
This code is little ugly because, as mentioned, only 1 method uses the parameter N.
If I instantiate Bar 100 times each with different N's, then doesn't that bloat the code? Like I said, only 1 method uses the parameter N, yet my understanding is that I'll get 99*(M-1) extra copies of effectively the same method.
That being said, is it standard to use a vector or a C-array instead to avoid template? Will I be sacrificing any potential performance?
//bar.h
template<int N>
class Bar
{
public:
Bar(const std::array<Foo, N>& arr);
void method_one();
void method_two();
void method_three();
...
private:
const std::array<Foo, N> arr_;
};
template<int N> Bar<N>::Bar(const std::array<Foo, N>& arr) :
arr_(arr)
{}
template<int N> void Bar<N>::method_one()
{
//....
}
template<int N> void Bar<N>::method_two()
{
//....
}
//etc
First, your compiler might (or might be induced to) fold identical functions even if they have different signatures.
Whether that is legal (and how to force it), see here:
Do distinct functions have distinct addresses?
Is an implementation allowed to site two identical function definitions at the same address, or not?
Next, if members of your template are independent of template-arguments, consider moving them to a base-class:
class Bar_base {
// Move everything here not dependent on template-arguments.
// Can be done multiple times if useful
}
template<int N> class Bar : public Bar_base {
// Everything dependent on `N`, and maybe some using-declarations
// for proper overloading
}
All implementations of the standard library do it extensively, and if you look at <iostream>, it's even codified in the standard.
If only the constructor uses std::array<...,N> you may turn your templated class into a standard class with templated constructor. Further more I would hope that the compiler is clever enough to not copy 100s of code fragment that do not have any dependency on N.
class Bar
{
public:
template<int N>
Bar(const std::array<Foo, N>& arr);
...
...
}
EDIT: Crap !! This doesn't seem to compile... It works with parameters like
class Bar {
public:
template<int N>
Bar(const int (& a)[N]);

Inheritance vs Specialization

Considering the following two usage scenarios (exactly as you see them, that is, the end-user will only be interested in using Vector2_t and Vector3_t):
[1]Inheritance:
template<typename T, size_t N> struct VectorBase
{
};
template<typename T> struct Vector2 : VectorBase<T, 2>
{
};
template<typename T> struct Vector3 : VectorBase<T, 3>
{
};
typedef Vector2<float> Vector2_t;
typedef Vector3<float> Vector3_t;
[2]Specialization:
template<typename T, size_t N> struct Vector
{
};
template<typename T> struct Vector<T, 2>
{
};
template<typename T> struct Vector<T, 3>
{
};
typedef Vector<float, 2> Vector2_t;
typedef Vector<float, 3> Vector3_t;
I can't make up my mind as to which is a nicer solution.
The obvious advantage to inheritance is code reuse in the derived classes; a possible disadvantage being performance (bigger size, users may pass by value, etc).
Specialization seems to avoid all that, but at the expense of me having to repeat myself multiple times.
What other advantages/disadvantages did I miss, and in your opinion, which route should I take?
What you ultimately want, i think, is to have the user type
Vector<T, N>
And depending on N, the user will get slight different things. The first will not fulfill that, but the second will, on the price of code duplication.
What you can do is to invert the inheritance:
template<typename T, size_t N> struct VectorBase
{
};
template<typename T> struct VectorBase<T, 2>
{
};
template<typename T> struct VectorBase<T, 3>
{
};
template<typename T, size_t N> struct Vector : VectorBase<T, N>
{
};
And implement the few functions that depend only on N being some specific value in the appropriate base-class. You may add a protected destructor into them, to prevent users deleting instances of Vector through pointers to VectorBase (normally they should not even be able to name VectorBase: Put those bases in some implementation namespace, like detail).
Another idea is to combine this solution with the one mentioned in another answer. Inherit privately (instead of publicly as above) and add wrapper functions into the derived class that call the implementations of the base-class.
Yet another idea is to use just one class and then enable_if (using boost::enable_if) to enable or disable them for particular values of N, or use a int-to-type transformer like this which is much simplier
struct anyi { };
template<size_t N> struct i2t : anyi { };
template<typename T, size_t N> struct Vector
{
// forward to the "real" function
void some_special_function() { some_special_function(i2t<N>()); }
private:
// case for N == 2
void some_special_function(i2t<2>) {
...
}
// case for N == 3
void some_special_function(i2t<3>) {
...
}
// general case
void some_special_function(anyi) {
...
}
};
That way, it is completely transparent to the user of Vector. It also won't add any space overhead for compilers doing the empty base class optimization (quite common).
Use inheritance and private inheritance. And don't use any virtual functions. Since with private inheritance, you don't have is-a, no one will be able to use a baseclas pointer to a derived subclass, and you won't get the slicing problem when passinfg by value.
This gives you the best of both worlds (and indeed it's how most libraries implement many of the STL classes).
From http://www.hackcraft.net/cpp/templateInheritance/ (discussing std::vector, not your Vector class):
vector<T*> is declared to have a
private base of vector<void*>.
All functions which place a new element
into the vector, such as push_back(), call the
equivalent function on this private base,
so internally our vector<T*> is using a
vector<void*> for storage. All functions
which return an element from the vector, such as
front(), perform a static_cast on the
result of calling the equivalent function
on the private base. Since the only way to get a
pointer into the vector<void*> (apart from
deliberately dangerous tricks) is through the
interface offered by vector<T*> it is safe
to staticly cast the void* back to T*
(or the void*& back to T*&, and so on).
In general, if the STL does it like this, it seems like a decent model to emulate.
Inheritance should only be used to model "is-a". Specialization would be the cleaner alternative. If you need or want to use inheritance for whatever reason, at the very least make it private or protected inheritance, so you don't inherit publicly from a class with a public non-virtual destructor.
Yes, the template metaprogramming guys always do
struct something : something_else {};
But those somethings are metafunctions and not meant to be used as types.
You need to decide the 'correct' answer to the following questions, based on your actual use case and what the real public interface should be:
Is "Vector<T,N>" your main public interface or an implementation detail? As in, are people supposed to use these objects like std::vector or is it simply a way to hide common implementation logic for the real API of "vector_2", etc.?
Will all "Vector<T,N>" have the same logical behavior? Most people will tell you to follow the STL when it comes to these types of decisions, but the STL itself infamously screwed this up with std::vector<bool>. And they've been forced to support the resulting mess for 30 years.
One thing to bear in mind is that you can combine both inheritance and template specialization; they're not mutually exclusive. And template specializations should be transparent to users, meaning they should have the same public interface and behavior as the general template.
For example:
template <typename T, size_t N>
class VectorBase {
public:
// The public interface all 'vector-like' classes must honor
virtual T& at(size_t index) = 0;
// More APIs...
};
// The generic Vector<T,N>
// Let's forbid inheritance from vectors, for now.
template <typename T, size_t N>
class Vector final : public VectorBase<T, N>
{
public:
// Have to implement the public interface
virtual T& at(size_t index) override;
// Vector-only APIs
virtual T* data();
};
// We've got some optimized way of storing, let's say doubles, that doesn't fit the general case
template <size_t N>
class Vector<double, N> final : public VectorBase<double, N>
{
public:
// Same APIs as baseline Vector<T,N>
virtual double& at(size_t index) override;
};
// We DISABLE a specialization, e.g. Vector<bool, N>
template <size_t N>
class Vector<bool, N> final : public VectorBase<bool, N>
{
}; // Nobody can ever create one of these. We never implemented the pure virtual APIs and we disabled inheritance.
// A different object that has a lot of interface overlap with Vector<T, N>
template <size_t N>
class Bitset : public Vector<bool, N>
{
public:
// From VectorBase
virtual bool& at(size_t index) override;
// Unique to me
virtual void bitwise_not();
virtual void bitwise_or(const Bitset<N>& other);
//...
};
To share code between the generic "Vector<T,N>" and its specializations, you have several options. The most straightforward way is the put it in protected functions of the base class.
If you're using template specialization too much, you probably need to rethink your design. Considering that you're hiding it behind a typedef, I doubt you need it.