I recently stumbled across expression templates in C++. There is one thing that I do not quite understand about their implementation, and that is why the base class is necessary (from which all other objects relating to template expression derive in a CRTP manner). A simple example that does addition and scalar multiplication on vectors (objects of type Vec, without base class):
#include <vector>
#include <iostream>
using namespace std;
class Vec
{
vector<double> data;
public:
template<typename E>
Vec(E expr)
{
data = vector<double>(expr.size());
for (int i = 0; i < expr.size(); i++)
data[i] = expr[i];
}
Vec(int size)
{
data = vector<double>(size);
for (int i = 0; i < size; i++)
data[i] = i;
}
double operator [] (int idx) {
return data[idx];
}
int size() { return data.size(); }
bool operator < (Vec &rhs)
{
return (*this)[0] < rhs[0];
}
bool operator > (Vec &rhs)
{
return (*this)[0] > rhs[0];
}
};
template<typename E1, typename E2>
class VecAdd
{
E1 vec_expr1;
E2 vec_expr2;
public:
VecAdd(E1 vec_expr1, E2 vec_expr2) : vec_expr1(vec_expr1), vec_expr2(vec_expr2)
{}
double operator [] (int idx) { return vec_expr1[idx] + vec_expr2[idx]; }
int size() { return vec_expr1.size(); }
};
template<typename E>
class ScalarMult
{
E vec_expr;
double scalar;
public:
ScalarMult(double scalar, E vec_expr) : scalar(scalar), vec_expr(vec_expr)
{}
double operator [] (int idx) { return scalar*vec_expr[idx]; }
int size() { return vec_expr.size(); }
};
template<typename E1, typename E2>
VecAdd<E1, E2> operator + (E1 vec_expr1, E2 vec_expr2)
{
return VecAdd<E1, E2>(vec_expr1, vec_expr2);
}
template<typename E>
ScalarMult<E> operator * (double scalar, E vec_expr)
{
return ScalarMult<E>(scalar, vec_expr);
}
int main()
{
Vec term1(5);
Vec term2(5);
Vec result = 6*(term1 + term2);
Vec result2 = 4 * (term1 + term2 + term1);
//vector<Vec> vec_vector = {result, result2}; does not compile
vector<Vec> vec_vector;
vec_vector = { result2, result }; //compiles
vec_vector.clear();
vec_vector.push_back(result);
vec_vector.push_back(result2); //all this compiles
for (int i = 0; i < result.size(); i++)
cout << result[i] << " ";
cout << endl;
system("pause");
return 0;
}
The code above compiles (except for the indicated line), and it evaluates the simple expressions in the main function without fault. If the expressions get assigned to an object of type Vec and assign their contents to a Vec object, getting destroyed in the process in any case, why is it necessary for a base class? (as shown in this Wikipedia article)
EDIT:
I know this code is a bit messy and bad (copying where unnecessary, etc.) but I am not planning on using this specific code. This is just to illustrate that expression templates work in this example without the CRTP base class - and I am trying to figure out exactly why this base class is necessary.
Your
template<typename E1, typename E2>
VecAdd<E1, E2> operator + (E1 vec_expr1, E2 vec_expr2)
will match for any user-defined types, not merely expression types. When instantiated with non-vector types, it will then likely fail. This interacts very badly with other C++ types, quite possibly including standard library types, which provide their own custom operator + and may rely on inexact matches resolving to their own operator + after implicit conversions.
Making operator + only available for VecExpression<E> avoids that problem.
Related
First I would like to apologize for the quality of my code, I'm just learning.
I have a university assignment.
String concatenation and adding one character to a string (like
on the left and on the right). Implement using overloading
the operator.
The question is this:
I need to implement two overloads (operator+)
First: adding one element to the end of the vector ( + 'e', for example ).
Second: adding an element to the beginning of the vector ('e' + , for example).
I have problems in order to implement the second part of the assignment.
I searched similar questions on stackoverflow, but they did not help me much.
Here is my code:
#include <iostream>
#include <vector>
using namespace std;
template <class T>
class String
{
private:
public:
vector<T> ptr_string;
String() // default value constructor (empty string)
{
ptr_string.push_back('\0');
}
String(const String& other) // copy constructor of the same type
{
int n = other.getLength();
for (int i = 0; i < n; i++)
{
ptr_string.push_back(other.ptr_string[i]);
}
}
String(T symbol, int n) // n times repeated value constructor
{
for (int i = 0; i < n; i++)
{
ptr_string.push_back(symbol);
}
}
String(String&& a) // move constructor
: ptr_string(a.ptr_string)
{
a.ptr_string = nullptr;
}
int getLength() const
{
return ptr_string.size();
}
void printString() const
{
int i = 0;
for (int i = 0; i < ptr_string.size(); i++)
{
cout << ptr_string[i];
}
cout << endl;
}
template <typename T2>
auto operator+(T2 b)
{
ptr_string.push_back(b);
return ptr_string;
}
auto operator+(String const& a)
{
ptr_string.push_back(a);
return ptr_string;
}
};
int main()
{
String<char> P = String<char>('P', 10);
P + 'e';
P.printString();
'e' + P;
P.printString();
}
I tried to pass a reference to a vector as a parameter, but I ran into a problem that this is most likely not the right solution.
auto operator+( String const& a)
{
ptr_string.push_back(a);
return ptr_string;
}
String<char> P = String<char>( 'P', 10);
'e' + P;
P.printString();
expected result: ePPPPPPPPPP
First, operator+ should not modify the current object. It should not return a vector but a new String<T>. It should be const.
Your char,int constuctor misses to add a nullterminator. Maybe that was on purpose, but I changed it because I am using that constructor. Moreover, I removed the move constructor, because its not needed yet. In its implementation you assign nullptr to the vector which is wrong. You need not implement the move constructor, you can declare it as =default;. This is what I also did for the copy constructor, because the compiler generated copy constructor is as good (or better) than your self written one.
Then, there is no + for 'e'+String in your code. When implmented as member then this is always the left operand. You can implement it as free function.
#include <iostream>
#include <vector>
template<class T>
class String {
public:
std::vector<T> ptr_string;
String() { ptr_string.push_back('\0'); }
String(const String& other) = default;
String(T symbol, int n) {
for (int i = 0; i < n; i++) {
ptr_string.push_back(symbol);
}
ptr_string.push_back('\0');
}
int getLength() const {
return ptr_string.size();
}
void printString() const {
int i = 0;
for (int i = 0; i < ptr_string.size(); i++) {
std::cout << ptr_string[i];
}
std::cout << std::endl;
}
template<typename T2>
auto operator+( T2 b) const {
String res = *this;
res.ptr_string.push_back(b);
return res;
}
auto operator+( String const& a) {
String res = *this;
for (const auto& c : a.ptr_string) res.ptr_string.push_back(c);
return res;
}
};
template<typename T2,typename T>
auto operator+(const T2& b,const String<T>& a) {
return String<T>(b,1) + a;
}
int main() {
String<char> P = String<char>( 'P', 10);
auto Y = P + 'e';
P.printString();
Y.printString();
auto Z = 'e' + P;
P.printString();
Z.printString();
}
Demo
This is just minimum changes on your code. I would actually implement also the other operators outside of the class as free functions. The loop in the char,int constructor should be replaced with the appropriate vector constructor and perhaps there is more which can be improved. For more on operator overloading I refer you to https://en.cppreference.com/w/cpp/language/operators and What are the basic rules and idioms for operator overloading?
I don't think your assignment really cover a scenario that happens in real life. It is full of code smell. As #463035818_is_not_a_number mentionned operator+ usually don't modify the current object. But it is technically possible, See below. Vector are not meant to "push_front", avoid 'push_front' on vector as a general rule (here, i use deque for ex).
#include <deque>
#include <iostream>
#include <string>
template<class T>
struct ConcatenableDeq
{
std::deque<T> _deq;
ConcatenableDeq(std::deque<T> deq) : _deq(deq) {};
void print() const
{
for (const auto& e : this->_deq)
std::cout << e << " ";
std::cout << std::endl;
}
friend void operator+(ConcatenableDeq<T>& cd, const T& v)
{
cd._deq.push_back(v);
}
friend void operator+(const T& v, ConcatenableDeq<T>& cd)
{
cd._deq.push_front(v);
}
};
int main(int argc, char* argv[])
{
ConcatenableDeq<char> cd({ '1', '2', '3' });
cd.print();
cd + '4';
cd.print();
'0' + cd;
cd.print();
// output:
// 1 2 3
// 1 2 3 4
// 0 1 2 3 4
}
This is my operator overload for multiplying a vector by an int:
template <typename T>
int& vector<T>::operator*(const int& b) {
int ans = 0;
for (int i = 0; i < this->size(); i++) {
ans += this[i] * b;
}
return ans;
}
This is my overload for vector*vector that should return dot product:
template <typename T>
int vector<T>::operator*(const vector<T>& b) const {
int t = 0;
for (int i = 0; i < b.size(); i++)
t += (this[i] * b[i]);
return t;
}
No matter how I have defined or declared it, I get an error
No operator "*" matches these operands
operand types are: int * PIC10b::vector<int>
PIC10b is the name of the namespace I've written to contain my vector.
The compiler doesn't understand the first block of code because you're trying to return an int when the function says it will return a reference to an int.
int vector<T>::operator*(const int& b) {
I want to implement an abstract Matrix (template) class and implement a lazy implementation first. Later on I want to implement a more performance oriented version of this class and want to use it in my whole project without changing everything.
The current problem is, that I am running in problems while implementing the +-operator.
The code below is one iteration, but i tried many different possibilities. But either I get an C2259 "Could not create instance of abstract class" like in the example below or I get runtime problems (Access violations while returning a reference or pointer).
I am sure that I am missing an easy and stupid point (again).
AbstMatrix.cpp:
#pragma once
#include "stdafx.h"
#include "Matrix.hpp"
template<typename T>
class AbstMatrix // : public AddMultEnabled<AbstMatrix<T>>
{
public:
inline virtual size_t getNRows() const = 0;
inline virtual size_t getNCols() const = 0;
inline size_t getNEle() const { return this->getNCols() * this->getNRows(); }
inline virtual T get(size_t iRow, size_t iCol) const = 0;
inline virtual void set(size_t iRow, size_t iCol, T val) = 0;
// Element wise addition
virtual AbstMatrix<T>& operator+=(const AbstMatrix<T>& obj) {
cout << "AM: op+=" << endl;
if (this->getNRows() != obj->getNRows()
|| this->getNCols() != obj->getNCols()) {
throw "Matricies unequal";
}
for (size_t i = 0; i < this->getNRows(); i++) {
for (size_t j = 0; j < this->getNCols(); j++) {
this->set(i, j, this->get(i, j) + obj->get(i, j));
}
}
return *this;
}
// Elementwise addition
virtual AbstMatrix<T> operator+(const AbstMatrix<T>& obj) const {
cout << "AM: op+" << endl;
Matrix<T> retM(*this);
return retM += obj;
}
};
Matrix.cpp:
#pragma once
#include "stdafx.h"
#include <algorithm>
#include "AbstMatrix.hpp"
template<typename T>
class Matrix : public AbstMatrix<T>
{
protected:
size_t nRows;
size_t nCols;
size_t nEle;
T* dat;
public:
Matrix(size_t nRows, size_t nCols, T defVal = 0) {
this->nRows = nRows;
this->nCols = nCols;
this->nEle = nCols*nRows;
this->dat = new T[this->getNEle()];
std::fill_n(this->dat, this->getNEle(), defVal);
}
Matrix(const AbstMatrix& obj) {
cout << "M: abst cpy:" << &obj << endl;
this->nRows = obj.getNRows();
this->nCols = obj.getNCols();
this->nEle = obj.getNEle();
this->dat = new T[this->getNEle()];
for (size_t i = 0; i < this->getNRows(); i++) {
for (size_t j = 0; j < this->getNCols(); j++) {
this->set(i, j, obj.get(i, j));
}
}
}
Matrix & operator= (const AbstMatrix & obj) {
this->nRows = obj.getNRows();
this->nCols = obj.getNCols();
this->nEle = obj.getNEle();
this->dat = new T[this->getNEle()];
for (size_t i = 0; i < this->getNRows(); i++) {
for (size_t j = 0; j < this->getNCols(); j++) {
this->set(i, j, obj.get(i, j));
}
}
}
~Matrix() { if (this->dat) delete[] this->dat; }
inline size_t getNRows() const { return this->nRows; }
inline size_t getNCols() const { return this->nCols; }
inline size_t getNEle() const { return this->nEle; }
inline T get(size_t iRow, size_t iCol) const {
cout << "M: get " << iRow << ", " << iCol << endl;
return this->dat[this->getIdx(iRow, iCol)];
}
inline void set(size_t iRow, size_t iCol, T val) {
cout << "M: set " << iRow << ", " << iCol << endl;
this->dat[this->getIdx(iRow, iCol)] = val;
}
inline AbstMatrix* clone() const {
cout << "M: clone " << endl;
return new Matrix(*this);
}
protected:
size_t getIdx(size_t iCol, size_t iRow) const {
cout << "M: getIdx " << iRow << ", " << iCol << ", "
<< (size_t) (this->getNCols() * iRow + iCol) << endl;
return this->getNCols() * iRow + iCol;
}
};
main.cpp:
#include "stdafx.h"
#include "Matrix.hpp"
int main()
{
Matrix<float> a(5, 5);
Matrix<float> b(5, 5);
a + b;
return 0;
}
Thank you a lot for your help!
[EDIT:] I fixed the (copy-paste) errors mentioned below. Matrix has now a copy and a move constructor. I added the following code at the bottom of AbstMatrix:
namespace detail {
template <typename T>
T AbstMatrix_ElemType(const AbstMatrix<T>&) { return T(); }
}
template <typename M1, typename M2>
auto operator+(M1 obj1, const M2& obj2)
-> std::enable_if_t<
std::is_same<decltype(detail::AbstMatrix_ElemType(obj1)),
decltype(detail::AbstMatrix_ElemType(obj2))>::value,
M1> {
return obj1 += obj2;
}
template <typename M1, typename M2>
auto operator*(M1 obj1, const M2& obj2)
-> std::enable_if_t<
std::is_same<decltype(detail::AbstMatrix_ElemType(obj1)),
decltype(detail::AbstMatrix_ElemType(obj2))>::value,
M1> {
return obj1 *= obj2;
}
// Mat multiplication
template <typename M1, typename M2>
auto mult(M1 obj1, const M2& obj2)
-> std::enable_if_t<
std::is_same<decltype(detail::AbstMatrix_ElemType(obj1)),
decltype(detail::AbstMatrix_ElemType(obj2))>::value,
M1> {
cout << "AM: mult" << endl;
if (obj1.getNCols() != obj2.getNRows()) {
throw("Matricies incompatible");
}
typedef decltype(detail::AbstMatrix_ElemType(obj1)) matValueType;
M1 retM(obj1.getNRows(), obj2.getNCols());
for (size_t i = 0; i < obj1.getNRows(); i++) {
for (size_t j = 0; j < obj2.getNCols(); j++) {
matValueType tmp = 0;
for (size_t x = 0; x < obj1.getNCols(); x++) {
tmp += obj1.get(i, x) * obj2.get(x, j);
}
retM.set(i, j, tmp);
}
}
return retM;
}
This works perfectly for me. I unfortunately still don't understand why this code works. I tried to read the doc at cppreference, but it just confused me. Do you have a easier source where I can understand the code?
Thanks a lot #aschepler!
// Elementwise addition
virtual AbstMatrix<T> operator+(const AbstMatrix<T>& obj) const {
cout << "AM: op+" << endl;
Matrix<T> retM(*this);
return retM += obj;
}
You cannot use an abstract class such as AbstMatrix<T> as a return type, since that involves creating an object of exactly that type. Also, your operator+ implementation relies on a specific subclass Matrix<T>. Generally a base class should not know anything about its derived classes (unless you're using CRTP).
Instead, you can define an operator+ template outside the class that acts on any two objects that inherit the same specialization of AbstMatrix, and returns the LHS type:
#include <type_traits>
namespace detail {
template <typename T>
T AbstMatrix_ElemType(const AbstMatrix<T>&);
}
template <typename M1, typename M2>
auto operator+(M1 obj1, const M2& obj2)
-> std::enable_if_t<
std::is_same<decltype(detail::AbstMatrix_ElemType(obj1)),
decltype(detail::AbstMatrix_ElemTYpe(obj2))>::value,
M1>
{ return obj1 += obj2; }
virtual AbstMatrix<T> operator+(const AbstMatrix<T>& obj) const {
cout << "AM: op+" << endl;
Matrix<T> retM(*this);
return retM += obj;
}
You cannot write such an operator that returns an abstract class object. Simply put, your operator says my method returns an instance of type AbstMatrix, but such an instance cannot exist for an abstract class. You can only have actual instances of derived, concrete (non-abstract) classes, and hold on them a reference (AbstMatrix<T>&) or a pointer (AbstMatrix<T>*).
You are actually creating an instance of the derived type Matrix<T> but to match the function's prototype, the return statement is forced to convert it to an AbstMatrix<T> instance (a mechanism called object slicing; it has to create an instance of AbstMatrix<T> and invoke the copy constructor). Since it cannot create such an instance, a compile error occurs.
At best you can make your operator+ to return a Matrix<T> object. But then the whole idea of having an abstract base class, that depends so strictly on one and only one of its derived classes, is not a good design idea. May be you should re-think the design and drop the idea of an abstract matrix class, bu only make it a class template.
Abstract interfaces used like this are not suitable for value-semantic computing.
You cannot return an instance of an abstract class. Actual instances -- values -- are of fixed storage size and type in C++. Derives instances of abstract interfaces won't "fit".
There are a few ways around this. One way is to implement your own polymorphism.
Start with template<class T>class AbstMatrix like you have. Get rid of operators -- only expose pure virtual methods. Virtual operators "work", but are awkward to use, so do not bother. Include virtual std::unique_ptr<AbstMatrix>clone()const=0 Consider eliminating other virtual methods; only include the ones you need. See below. You'll want at(x,y), clone(), increment_by(AbstMatrix), mult_by(AbstMatrix), etc.
Next write Matrix<T>. This does not inherit from AbstMatrix<T>, but instead owns a std::unique_ptr<AbstMatrix<T>> pImpl;. It can be construxted from a unique_ptr<AbstMatrix<T>> as well. It can be "empty"; its methods ahould not assume pImpl is non-null.
This Matrix<T> type implements operator+=, operator+, operator=, Matrix(Matris const&), Matrix(Matris&&)=default, etc. Its job is to be a value semantics type that is polymorphic because its behaviour is determined by the abstract class it owns in a unique ptr.
This lets you have a polymorphic value type.
Now your implementations inherit from AbstMatrix<T> and can be stored in a Matrix<T>.
I have a self-defined Matrix class and want to overload operator * to do matrix multiplication:
template< int R, int C>
class Matrix{
int *_mat;
int _size;
public:
Matrix(){ _size = R*C; _mat = new int[_size]{0}; }
~Matrix(){ delete []_mat; }
Matrix &operator=(const Matrix & m){/*...*/}
//...
template< int D2, int D1 > using matrix_t = int[D2][D1];
template<int R2, int C2>
Matrix<R,C2> operator*(const matrix_t<R2,C2> &mat)
{
Matrix<R,C2> result;
for(int r = 0; r < R; r++)
{
for(int c = 0; c < C2; c++)
{
for( int i; i < C; i++ ){
/*do multiplication...
result._mat[r*C2+c] = ...
*/
}
}
}
return result;
}
//...
};
Then the problem comes with Matrix<R,C2> result. The result becomes a outside object of the class. So I cannot access its private member using like result._mat[r*C2+c].
What is the solution( without changing access permission) to define my function of matrix multiplication in this class?
You could specify an operator so you can externally set the values of the matrix. Note you won't be able to use operator [] - since you can only use that with one argument (ref C++ [] array operator with multiple arguments?)
int& operator() (int row, int col) {
// todo: check array bounds
return _mat[C*row+col];
}
Usage:
result(r,c) = ...
You cannot, you can just write function like set
void set(int index, int value)
{
// check index
_mat[index] = value;
}
And then in multiplication function just call result.set(...). Instead of
result._mat[r*C2+c] = ...
just
result.set(r*C2+c, ...);
This situation is since Result is object of type Matrix<R, C2>, that is not the same type as Matrix<R, C>, so you cannot access private members of type Matrix<R, C2> in member function of type Matrix<R, C>.
I have been working on a priority queue using a binary heap and have developed a class for this as shown below.
#include <iostream>
#include <type_traits>
template<class T, int N>
class BinaryHeap{
template<class T1>
class has_less_than_operator
{
private:
class no{};
template<class X>
static auto has(X&& t) -> decltype (t.operator < (t));
static no has(...);
public:
enum {
value = !std::is_same<
decltype(has( std::declval<T1>() )),
no>::value
};
};
static_assert(std::is_copy_assignable<T>::value &&
std::is_copy_constructible<T>::value,
"Must be copy assignable and constructable");
public:
BinaryHeap() : used_(0){
}
BinaryHeap(BinaryHeap const& other) = default;
BinaryHeap& operator = (BinaryHeap const& other) = default;
inline T& max(){
return elements_[FIRST];
}
inline T const & max() const{
return elements_[FIRST];
}
void insert(T const& item){
elements_[++used_] = item;
swim(used_);
}
inline bool full() const{
return used_ == N;
}
void deleteMax(){
std::swap(elements_[used_],elements_[FIRST]);
sink(FIRST);
elements_[used_--] = T();
}
private:
template<class T1>
class has_dereference_operator
{
private:
class no{};
template<class X>
static auto has(X&& t) -> decltype (t.operator * ());
static no has(...);
public:
enum {
value = !std::is_same<
decltype(has( std::declval<T1>() )),
no>::value
};
};
inline bool parent_less(int position,std::integral_constant<int,0> i){
return elements_[ position / 2] < elements_[position];
}
inline bool parent_less(int position,std::integral_constant<int,1> i){
return *(elements_[ position / 2]) < *(elements_[position]);
}
void swim(int position){
while(position > 1 && parent_less(position,std::integral_constant<int, has_dereference_operator<T>::value>()))
{
std::swap(elements_[ position / 2], elements_[position]);
position /= 2;
}
}
inline int which_less(int p1, int p2, std::integral_constant<int,0> i){
return (elements_[ p1] < elements_[p2]) ? p1 : p2;
}
inline int which_less(int p1, int p2, std::integral_constant<int,1> i){
return (*(elements_[ p1]) < *(elements_[p2])) ? p1 : p2;
}
inline int which_greater(int p1, int p2, std::integral_constant<int,0> i){
return (elements_[ p1] < elements_[p2]) ? p2 : p1;
}
inline int which_greater(int p1, int p2, std::integral_constant<int,1> i){
return (*(elements_[ p1]) < *(elements_[p2])) ? p2 : p1;
}
void sink(int position){
while(position * 2 <= used_){
int first = position * 2;
if(first > used_) break;
int greater_child = which_greater(first, first + 1, std::integral_constant<int, has_dereference_operator<T>::value>());
int lesser = which_less(greater_child, position, std::integral_constant<int, has_dereference_operator<T>::value>());
if(lesser == greater_child)
break;
std::swap(elements_[greater_child], elements_[position]);
position = greater_child;
}
}
inline int current_position() const{
return used_ + 1;
}
static const int MAX = N + 1;
static const int FIRST = 1;
static const int LAST = N;
T elements_[MAX];
int used_;
};
int main(int argc, const char * argv[])
{
BinaryHeap<int, 10> b;
b.insert(1);
b.insert(20);
b.insert(21);
b.insert(3);
b.insert(2);
std::cout << "Max: " << b.max() << std::endl;
b.deleteMax();
std::cout << "Max: " << b.max() << std::endl;
return 0;
}
Although I have this working I need to deal with the differences in comparing say a pointer / shared pointer say using dereference operator and values just using them as is. I am currently using SFINAE to do this based on if the class has operator *.
Is this the right way to achieve this?
Blair
The problem with using heuristics like this is that it's not always what the client of your code wants you to do, and you don't provide a way to change the behavior. Someday a client may want to use your class to store pointers and actually sort them with std::less<T> instead of dereferencing (BinaryHeap<void*,32> for example). Even with non-pointers, a client may simply want a differing ordering than that imposed by <.
When the Standard Library needs to perform comparison it usually uses std::less<T> by default but provides a way for the client to override that choice (e.g. std::priority_queue or std::sort). If I was writing your class, I would parameterize it by comparison operator defaulting to std::less<T> just like the Standard Library would. I would also provide a handy dereferencing comparator template to make it easy for clients using pointers to order by pointees.