Removing a member based on a template parameter - c++

I would like to design a vector class with small vector optimisation. It looks like:
template <typename T, int small_size = 0>
class Vector {
private:
T data_small_[small_size];
T* data_;
T* size_;
T* capacity_;
public:
...
}
Unfortunately, most of the time, the class will be used with small_size = 0. Is there a way to remove data_small_ for small_size = 0 without going into template specialisation and rewriting the whole code for the class?

You can use the empty base optimization here. You would have to change Vector to not refer to data_small_ directly. Instead, write use SmallData to implement the required member functions and handle the small_size = 0 case in the partial specialization SmallData<T, 0>. Below is an example with SmallData implementing a copy constructor and operator[] and Vector using them without worrying about small_size.
template <typename T, int small_size>
class SmallData
{
public:
SmallData(const SmallData& other)
{
for(size_t i = 0; i < small_size; i++)
data_small_[i] = other.data_small_[i];
}
T& operator[](int k){return data_small_[k];}
protected:
T data_small_[small_size];
};
template <typename T>
class SmallData<T, 0>
{
public:
T& operator[](int k){//throw some error}
};
template <typename T, int small_size = 0>
class Vector : public SmallData<T, small_size>
{
public:
Vector(const Vector& other) : SmallData<T, small_size>(other)
{
//rest of copy ctor either here on in the member init list above
}
T& operator[](int k)
{
if(k<small_size) return SmallData<T, small_size>::operator[](k);
else return data_[k];
}
};

Related

How can I extract a nested type from a child class and use it in the function signature of a virtual function in the abstract parent class?

I frequently have to implement "container-like" classes in C++ and I find that I often forget to implement one part of the interface and have problems later. To fix this I want to create an abstract Container class that I can make my container classes inherit from that will fail to compile if any of the required interface functions are not implemented.
The issue is container classes typically define a size_type and I want the size() function to return the corresponding size_type of whatever the underlying container is.
Here is a simplified example of what I am talking about:
#include <vector>
// Abstract class
template <typename T>
class Container
{
public:
virtual ~Container() = 0;
//********************************************************************************
// How do I tell it to return whatever "size_type" is for the child class?
//********************************************************************************
virtual typename ChildClass::size_type size() const = 0;
};
template <typename T>
Container<T>::~Container() = default;
// Concrete
template <typename T>
class MyVector final : public Container<T>
{
public:
// size_type of the child class defined here
using size_type = typename std::vector<T>::size_type;
MyVector()
: m_data()
{}
size_type size() const override
{
return m_data.size();
}
private:
std::vector<T> m_data;
};
Is there any way to reference the size_type of whatever the child class is in the abstract function definition of size()?
EDIT
After applying the CRTP (recommended below) I feel like I am much closer, but it is still failing to compile the second I try to instantiate an object of MyVector<double>:
// Abstract class
template <typename T>
class Container {
public:
virtual ~Container() = default;
// Still can't seem to resolve the type here
virtual typename T::size_type size() const = 0;
};
// Concrete
template <typename T>
class MyVector final : private Container<MyVector<T>> {
public:
using size_type = typename std::vector<T>::size_type;
using value_type = T;
size_type size() const
{
return m_data.size()
}
private:
std::vector<T> m_data;
};
int main()
{
// Fails to compile
MyVector<double> v;
}
I get the compiler error:
error C2039: 'size_type': is not a member of 'MyVector'
From the answer to a related question I figured out something that works. It's a bit ugly since it requires me to define a ContainerTraits struct for each container I want to implement, but it does get the job done.
template <typename T>
struct ContainerTraits;
// Abstract class
template <typename T>
class Container {
public:
using size_type = typename ContainerTraits<T>::size_type;
virtual ~Container() = default;
virtual typename size_type size() const = 0;
};
// Concrete
template <typename T>
class MyVector final : private Container<MyVector<T>> {
public:
using size_type = typename ContainerTraits<MyVector>::size_type;
size_type size() const override { return m_data.size(); }
private:
std::vector<T> m_data;
};
template <typename T>
struct ContainerTraits<MyVector<T>>
{
using size_type = typename std::vector<T>::size_type;
};
I was thinking... how do you pass typenames to a class? As template parameter!
So you could do something like this:
#include <vector>
// interface
template <
/*typename container_type, // not required in this example */
typename size_type
>
class Container {
public:
virtual ~Container() = default;
virtual size_type size() const = 0;
};
template <
typename value_type,
typename size_type = typename std::vector<value_type>::size_type
>
class MyVector final :
public Container<
/*MyVector<T>,*/
size_type
>
{
public:
size_type size() const
{
return m_data.size();
}
private:
std::vector<value_type> m_data;
};
int main()
{
MyVector<double> v;
}

Generic Array with a generic size C++

this is my generic class Array, I would like to ask about the copy constructor and assignment, is this the right way to do it? If yes, then do I really need to insert both of them in the class?
Thank you in advance.
template <class T, int SIZE>
class Array {
T data[SIZE];
public:
explicit Array();
Array(const Array& a); //copy constructor
~Array(); //destructor
Array& operator=(const Array& a); //assignment
T& operator[](int index);
const T& operator[](int index) const;
};
template <class T,int SIZE>
Array<T,SIZE>::Array()
{
}
template <class T,int SIZE>
Array<T,SIZE>::~Array()
{
}
template <class T,int SIZE>
Array<T,SIZE>::Array& operator=(const Array& a)
{
for(int i=0;i<SIZE;i++)
{
data[i]=a[i];
}
return *this;
}
template <class T,int SIZE>
Array<T,SIZE>::Array(const Array& a)
{
for(int i=0;i<SIZE;i++)
{
data[i]=a[i];
}
}
In this case, you can apply the Rule of Zero:
template <class T, int SIZE>
class Array {
T data[SIZE];
public:
T& operator[](int index);
const T& operator[](int index) const;
};
The compiler will generate the functions for you. If you need to do something custom in them, then yes, you need to define them.
In that case, the signature of your assignment operator needs to be fixed to (i.e. your posted code does not compile):
template <class T,int SIZE>
Array<T,SIZE>& Array<T,SIZE>::operator=(const Array& a)
Then, you should be directly accessing the other array's data, instead of using its operator[] (unless you have a reason):
data[i] = a.data[i]; // note `a.data[i]` vs. `a[i]`
Moreover, you can take advantage of the compiler to avoid writing loops. Simply wrap your array into a struct:
template <class T, int SIZE>
class Array {
struct S {
T data[SIZE];
} s;
// ...
};
So that you can replace your loops with:
s = a.s;
Not only that, but using the struct will allow you to copy-construct the elements of the array, rather than copy-assigning them (as you were doing with the loop), which may be important for some T types:
template <class T,int SIZE>
Array<T,SIZE>::Array(const Array& a)
: s(a.s) // note the member initializer
{
// empty body
}
This solves the compiler error:
template <class T,int SIZE>
auto Array<T,SIZE>::operator=(const Array& a) -> Array&
{
for(int i=0;i<SIZE;i++)
{
data[i]=a[i];
}
return *this;
}
There is a separate discussion to be had about redundant initialisation of the array elements in the constructor.

Reduce code duplication in class template specialization (array<Unique_ptr>)

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

C++ template class overload [] operator

I would like to overload the [] operator for a template class in respect with the template parameters. Like so:
template<
typename T,
template<typename> class Property,
template<typename> class Key1,
template<typename> class Key2>
class a_map
{
public:
const Property<T>& operator[](const Key1<T>& k) const
{ return _values[k.index()]; }
const Property<T>& operator[](const Key2<T>& k) const
{ return _values[k.index()]; }
protected:
std::vector<Property<T> > _values;
};
I would use this class like so:
int main()
{
a_map<float, prop, key_a, key_b> pm;
}
Basically I want to be able to access the elements inside the _values vector without having to worry to much about the Key types. All that matters is that they have an index() member.
However I get the following error
error C2535: 'const Property &a_map::operator
[](const Key1 &) const' : member function already defined or
declared
even though key_a and key_b are two totaly different types class templates.
Am I missing something? Is the compiler afraid that in certain situation Key1<T> and Key2<T> might actually be the same type?
Edit
These are the class templates used in main
template<typename T>
struct prop
{
T weight;
T height;
};
template<typename T>
class key_a
{
public:
int index() { return _i; }
private:
int _i;
};
template<typename T>
class key_b
{
public:
int index() { return 3; } // Always return 3
Edit
I'm using MVC++ 2008 compiler.
Since both of your operator[] are the same except for the argument type, why not template them?
template <typename TT>
const Property<T>& operator[](const TT& k) const
{
return _values[k.index()];
}
You need to declare your index() functions as const because inside the a_map template you are invoking them through const objects
template<typename T> class key_a {
public:
int index() const // <- `const` is necessary
{ return _i; }
private:
int _i;
};
template<typename T> class key_b {
public:
int index() const // <- `const` is necessary
{ return 3; }
};
But otherwise, everything compiles and work fine for me.
Tried it in VS2010 compiler and get the same error as yours. This is obviously a compiler bug in MSVC++ compilers. Handling of template-template arguments is implemented incorrectly.
To work around the problem I was able to use this technique
template<
typename T,
template<typename> class Property,
template<typename> class Key1,
template<typename> class Key2>
class a_map
{
public:
const Property<T>& operator[](const typename Key1<T>::self& k) const
{ return _values[k.index()]; }
const Property<T>& operator[](const typename Key2<T>::self& k) const
{ return _values[k.index()]; }
protected:
std::vector<Property<T> > _values;
};
and define the key templates as
template<typename T> class key_a {
public:
typedef key_a self;
int index() const { return _i; }
private:
int _i;
};
template<typename T> class key_b {
public:
typedef key_b self;
int index() const { return 3; }
};
This is inelegant, but it makes it work correctly with MSVC++ compilers.
Compiles fine like this... note how Prop/K1/K2 should be defined.
#include <vector>
template<
typename T,
template<typename> class Property,
template<typename> class Key1,
template<typename> class Key2>
class a_map
{
public:
const Property<T>& operator[](const Key1<T>& k) const
{ return _values[k.index()]; }
const Property<T>& operator[](const Key2<T>& k) const
{ return _values[k.index()]; }
protected:
std::vector<Property<T> > _values;
};
template <typename T> struct K1 { int index() const { return 0; } };
template <typename T> struct K2 { int index() const { return 0; } };
template <typename T> struct Prop { };
int main()
{
a_map<float, Prop, K1, K2> m;
}

Using STL algorithms (specifically std::sort) from within a templated class

I've declared a template class MyContainer as bellow, then created an instance of it of type DataType1. The DataType1 class provides a friend function "DataSpecificComparison" which is used by std::sort to compare DataType1 objects. The program compiled and sorted correctly.
I then defined a class called DataType2, gave it a friend implementation of "DataSpecificComparison" and used it to create another instance of MyContainer.
I am now unable to compile the program as a "C2914: 'std::sort' : cannot deduce template argument as function argument is ambiguous" compile time error is reported.
How can a developer specify that the DataSpecificComparison binary predicate is to take arguments of template type T*? Or is there another way around this issue?
template <class T>
class MyContainer
{
private:
vector<T*> m_vMyContainerObjects;
....
public:
....
void SortMyContainerObjects()
{
std::sort(m_vMyContainerObjects.begin(), m_vMyContainerObjects.end(), DataSpecificComparison)
}
}
class DataType1
{
....
friend bool DataSpecificComparison(const DataType1 * lhs, const DataType1 * rhs)
}
class DataType2
{
....
friend bool DataSpecificComparison(const DataType2* lhs, const DataType2* rhs)
}
You can use a temporary local function pointer variable of the required type to select the correct overload of DataSpecificComparison:
void SortMyContainerObjects()
{
typedef bool (*comparer_t)(const T*, const T*);
comparer_t cmp = &DataSpecificComparison;
std::sort(m_vMyContainerObjects.begin(), m_vMyContainerObjects.end(), cmp);
}
Here the compiler can deduce that you want to use the DataSpecificComparison overload that matches the comparer_t type, which resolves the ambiguity.
sth already gave a correct answer, but there's also a direct alternative based on the same principle:
void SortMyContainerObjects()
{
std::sort(m_vMyContainerObjects.begin(), m_vMyContainerObjects.end(),
static_cast<bool (*comparer_t)(const T*, const T*)>(&DataSpecificComparison));
}
This uses essentially the same mechanism. The cast forces overload resolution to happen before the Template Argument Deduction for std::sort.
template<typename T>
struct DataSpecificComp : public binary_function<T, T, bool>
{
public:
bool operator()(const T* lhs, const T* rhs)
{
return *lhs < *rhs;
}
};
call the sort function as shown below:
sort(vi.begin(), vi.end(), DataSpecificComp<int>());
I'd prefer something along the following lines: by default it compares objects with less_than (so you wouldn't have to remember to provide a function with a funny name), and there's an overload that allows giving your own comparison functor (again, value-based):
#include <vector>
#include <algorithm>
#include <functional>
template <class T, class Func>
struct indirect_binary_call_type: public std::binary_function<const T*, const T*, bool>
{
Func f;
indirect_binary_call_type(Func f): f(f) {}
bool operator()(const T* a, const T* b) const
{
return f(*a, *b);
}
};
template <class T, class Func>
indirect_binary_call_type<T, Func> indirect_binary_call(Func f)
{
return indirect_binary_call_type<T, Func>(f);
}
template <class T>
class MyContainer
{
private:
std::vector<T*> m_vMyContainerObjects;
public:
void Sort()
{
Sort(std::less<T>());
}
template <class Func>
void Sort(Func f )
{
std::sort(m_vMyContainerObjects.begin(), m_vMyContainerObjects.end(), indirect_binary_call<T>(f));
}
};
int main()
{
MyContainer<int> m;
m.Sort();
m.Sort(std::greater<int>());
}
Did you try defining DataSpecificComparison as template with bunch of specializations and giving it the type?
template<T>
bool DataSpecificComparison(const T* t1, const T* t2)
{
// something non compilable here
}
template<> bool DataSpecificComparison<Data1>(const Data1* t1, const Data1* t2)
{
// return *t1 < *t2;
}
....
void SortMyContainerObjects()
{
std::sort(m_vMyContainerObjects.begin(), m_vMyContainerObjects.end(), DataSpecificComparison<T>)
}
....
Templating DataSpecificComparison should work. You can also specifically call the proper std::sort template, but it's a bit cumbersome:
template <class T>
class MyContainer
{
private:
vector<T*> m_vMyContainerObjects;
typedef bool (*compsT)(T, T);
public:
....
void SortMyContainerObjects()
{
std::sort<std::vector<T*>::iterator, compsT>(m_vMyContainerObjects.begin(), m_vMyContainerObjects.end(), DataSpecificComparison);
}
}