Class template with enum specific implementation and shared functionality (CRTP) - c++

I want to design a Matrix class which allows to specify the method of memory management, i.e.
Matrix<Allocator::vector, int> mv(2, 2);
Matrix<Allocator::unique_pointer, int> mu(2, 2);
mv(1, 1) = 1;
mu(1, 2) = 1;
and with mv and mu being compatible to each other (i.e. when I overload the "+" operator) despite different memory strategies.
I found already very good help from Class template specializations with shared functionality which does the same for an n dimensional vector class.
using dim_t = std::pair<size_t, size_t>;
enum class Allocator { vector, raw_pointer, unique_pointer };
template <typename T>
class MatrixBase {
public:
MatrixBase() : MatrixBase(0, 0){};
MatrixBase(size_t m, size_t n) : dim_(m, n){};
size_t rows() const;
virtual T& at(size_t i, size_t j);
private:
dim_t dim_;
};
template <typename T>
size_t MatrixBase<T>::rows() const {
return dim().first;
}
template <Allocator A, typename T>
class Matrix : public MatrixBase<T> {};
template <typename T>
class Matrix<Allocator::vector, T> : public MatrixBase<T> {
private:
std::vector<T> data_;
};
template <typename T>
T& Matrix<Allocator::vector, T>::at(size_t i, size_t j) {
return data_[i * rows() + j];
}
template <typename T>
class Matrix<Allocator::unique_pointer, T> : public MatrixBase<T> {
private:
std::unique_ptr<T[]> data_;
};
template <typename T>
T& Matrix<Allocator::unique_pointer, T>::at(size_t i, size_t j) {
return data_[i * rows() + j];
}
Unfortunately the compiler complains
./matrix.hpp:100:34: error: out-of-line definition of 'at' does not match any declaration in 'Matrix<linalg::Allocator::vector,
type-parameter-0-0>'
T& Matrix<Allocator::vector, T>::at(size_t i, size_t j) {
^
./matrix.hpp:103:20: error: use of undeclared identifier 'rows'
return data_[i * rows() + j];
I assume the error originates from
template <typename T>
class Matrix<Allocator::vector, T> : public MatrixBase<T> {
private:
std::vector<T> data_;
};
How do I fix this?

I have fixed Your code. Here You go:
using dim_t = std::pair<size_t, size_t>;
enum class Allocator { vector, raw_pointer, unique_pointer };
template <typename T>
class MatrixBase {
public:
MatrixBase() : MatrixBase(0, 0){};
MatrixBase(size_t m, size_t n) : dim_(m, n){};
size_t rows() const;
virtual T& at(size_t i, size_t j);
private:
dim_t dim_;
};
template <typename T>
size_t MatrixBase<T>::rows() const {
return dim_.first;
}
template <Allocator A, typename T>
class Matrix : public MatrixBase<T> {};
template <typename T>
class Matrix<Allocator::vector, T> : public MatrixBase<T> {
public:
T &at(size_t i, size_t j);
private:
std::vector<T> data_;
};
template <typename T>
T& Matrix<Allocator::vector, T>::at(size_t i, size_t j) {
return data_[i * this->rows() + j];
}
template <typename T>
class Matrix<Allocator::unique_pointer, T> : public MatrixBase<T> {
public:
T &at(size_t i, size_t j);
private:
std::unique_ptr<T[]> data_;
};
template <typename T>
T& Matrix<Allocator::unique_pointer, T>::at(size_t i, size_t j) {
return data_[i * this->rows() + j];
}
First of all, if You override a method You have to declare it in derived class. Secondly, the rows() function could not be resolved by default as a member.

Move the definition inside will solve the problem. This problem is due to scoping.
template <typename T>
class Matrix<Allocator::vector, T> : public MatrixBase<T> {
public: // or private:
T& at(size_t i, size_t j) {
return data_[i * MatrixBase<T>::rows() + j];
}
private:
std::vector<T> data_;
};
Consider the following example
class P {
public:
virtual int test();
};
class C : public P {
};
int C::P:: test() { // if you omit P, it will not compile
return 21;
}
Or you can re-declare it inside C, so it will be in C's scope.

Related

C++ Templated Classes Definition - Templated Return Type of Different Class

template <class T>
Row<T> Array2D<T>::operator[](int index) const
{
}
This code is not working, giving the error 'Declaration is incompatible with "Array2D::Row Array2D::operator[](int index) const" (declared at line 19". I'm using 2 templated classes, and I figure that that is the main issue but I'm not sure how it is fixed. Any help is appreciated, let me know if I need more details. Thanks.
Class Declaration:
#pragma once
#include "Array.h"
#include "Row.h"
template <class T>
class Array2D
{
template <class T> class Row;
public:
Array2D();
Array2D(int row, int col);
Array2D(const Array2D & copy);
~Array2D();
Array2D & operator =(const Array2D & rhs);
Row<T> operator[](int index) const;
int getRow() const;
void setRow(int rows);
int getColumns() const;
void setColumns(int columns);
T & Select(int row, int column);
private:
Array<T> m_array;
int m_row;
int m_col;
};
Row Class:
#pragma once
#include "Array2D.h"
template <class T>
class Row
{
template <class T> class Array2D;
public:
Row(Array2D<T> & array, int row);
T & operator[](int column);
private:
Array2D<T> & m_array2D;
int m_row;
};
template <class T>
Row<T>::Row(Array2D<T> & array, int row)
{
}
template <class T>
T & Row<T>::operator[](int column)
{
}
If you write
template <class T>
class Array2D
{
template <class T> class Row;
// ...
};
you say that Row is a template class defined inside Array2D.
The same when you write
template <class T>
class Row
{
template <class T> class Array2D;
// ...
};
You saying that Array2D is a template class defined inside Row.
I suppose you intention declare Raw and Array2D as independent classes, so something as
template <class T>
class Row;
template <class T>
class Array2D
{
// ...
};
and
template <class T>
class Array2D;
template <class T>
class Row
{
// ...
};

Add a method to templated class for specific type

For example, I have a class template:
template <typename T>
class base {
public:
void set(T data) { data_=data; }
private:
T data_;
};
And for a certain type I would like to add a function, but also have functions from the template class.
template <>
class base<int>{
public:
void set(int data) { data_=data; }
int get(){ return data_;} //function specific to int
private:
int data_;
}
How to do that without copying all members from the template class?
With inheritance:
template <typename T> struct extra {};
template <> struct extra<int> {
public:
int get() const;
};
template <typename T>
class base : public extra<T> {
friend class extra<T>;
public:
void set(T data) { data_=data; }
private:
T data_ = 0;
};
int extra<int>::get() const{ return static_cast<const base<int>*>(this)->data_;}
Demo
You can do this by using enable_if from type_traits to enable the get function only when the template parameter is int. One example is shown below.
#include <type_traits>
template <typename T>
class base {
public:
template <typename X=T,
std::enable_if_t< std::is_same<X,typename T>::value
&& std::is_same<X,int>::value, bool> = false>
int get() { return data_; }
void set(T data) { data_=data; }
private:
T data_;
};

Removing a member based on a template parameter

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];
}
};

template dependent constructor argument lengths

I try to make a generic, but still efficient multi dimension Point class.
What I have is a Dimensions enum
enum Dimension : std::size_t { _2D = 2, _3D = 3 };
And a Point class
template <typename T, Dimension D>
class Point : public std::array<T, D>
{
public:
T& at(size_t idx) { return std::array<T,D>::at(idx); };
const T& at(size_t idx) const { return std::array<T,D>::at(idx); };
Dimension dim() const { return D; }
...
};
I would like to create nices constructors so I added (outside my class definition)
template <typename T>
Point<T,_2D>::Point(T x, T y) { at(0) = x; at(1) = y; }
template <typename T>
Point<T,_3D>::Point(T x, T y, T z) { at(0) = x; at(1) = y; at(2) = z; }
But still I cannot use thoses. The compiler tells me only default (empty) and copy constructors are registered.
Question:
How do I define constructors with arguments list length being dependant on my Dimension template ?
I would do it like this because I'm too lazy to write many versions of the same thing:
template<class T, Dimension D>
class Point : public std::array<T,D> {
template<class... Args>
Point(Args... vs) :
std::array<T,D>{{vs...}}
{
static_assert(sizeof...(Args) == D, "wrong number of args");
}
...
};
There are a few ways to accomplish this. In your case, it might be easiest to use std::enable_if:
template <typename T, Dimension D>
class Point : public std::array<T, D>
{
public:
template <
Dimension D2=D,
typename = typename std::enable_if<D2==_2D>::type
>
Point(T x,T y);
template <
Dimension D2=D,
typename = typename std::enable_if<D2==_3D>::type
>
Point(T x,T y,T z);
T& at(size_t idx) { return std::array<T,D>::at(idx); };
const T& at(size_t idx) const { return std::array<T,D>::at(idx); };
Dimension dim() const { return D; }
...
};
Another option would be to break this into multiple classes and use specialization:
template <typename T, Dimension D>
class PointBase : public std::array<T, D>
{
public:
T& at(size_t idx) { return std::array<T,D>::at(idx); };
const T& at(size_t idx) const { return std::array<T,D>::at(idx); };
Dimension dim() const { return D; }
...
};
template <typename T, Dimension D> class Point;
template <typename T>
class Point<T,_2D> : public PointBase<T,_2D> {
public:
Point(T x,T y);
};
template <typename T>
class Point<T,_3D> : public PointBase<T,_3D> {
public:
Point(T x,T y,T z);
};

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