I was looking at the example of expression templates at https://en.wikipedia.org/wiki/Expression_templates. Then I tried to make a simple symbolic expression tree, i.e. to add constants and variables like a + b + 10. So I started with
#include <iostream>
template<typename E>
class Expression {
public:
std::ostream& print(std::ostream& os) const
{
return expression().print(os);
}
E const& expression() const { return static_cast<E const&>(*this); }
};
class Var : public Expression<Var> {
public:
Var(const char name)
: name_(name)
{}
std::ostream& print(std::ostream& os) const
{
return os << name_;
}
private:
const char name_;
};
class Constant : public Expression<Constant> {
public:
Constant(const double value)
: value_(value)
{}
std::ostream& print(std::ostream& os) const
{
return os << value_;
}
private:
const double value_;
};
template<typename E1, typename E2>
class ExpressionSum : public Expression<ExpressionSum<E1,E2>> {
E1 const& u_;
E2 const& v_;
public:
ExpressionSum(E1 const& u, E2 const& v) : u_(u), v_(v)
{
}
std::ostream& print(std::ostream& os) const
{
os << "(";
u_.print(os);
os << " + ";
v_.print(os);
os << ")";
return os;
}
};
template <typename E1, typename E2>
ExpressionSum<E1,E2> operator+(Expression<E1> const& u, Expression<E2> const& v) {
return ExpressionSum<E1, E2>(u.expression(), v.expression());
}
int main() {
Var a('a');
Var b('b');
Constant c(1.0);
auto expr = a + b + c;
expr.print(std::cout);
std::cout << std::endl;
auto expr2 = expr + Constant{2.0};
expr2.print(std::cout);
std::cout << std::endl;
}
The expression expr is fine, but I cannot reuse expr to build another expression like expr2 since the temporary ExpressionSum of a+b is already destroyed. Is there a way to store these temporaries in the expression?
Avoid CRTP: Use Argument-Dependent Lookup to simplify the library
We want to keep things as simple as possible. The Curiously Recurring Template Pattern (and it's relatives) are powerful tools, but they increase compile-times and are cumbersome when you want to expand what you're doing.
By taking advantage of argument dependent lookup, we can implement operator overloading without having a base class. This greatly simplifies the design of the library. I'll explain more about this in the examples given below
Avoid lifetime issues: store subexpressions by value unless explicitly using std::ref
We want to keep this library simple. An expression is either a constant, a unary operation and an input, or a binary operation and an input. There aren't any class invariants - the inputs can take on any value, and the operation itself is stored based on it's type, so it can only have 1 value.
This means that we can represent expressions as aggregate types, making them trivially constructible, trivially copyable, trivially destructible, and reducing both compiletimes and the size of the resulting binary.
namespace expr // We need to put them in a namespace so we can use ADL
{
template<class Value>
class Constant
{
public:
Value value;
};
template<class Op, class Input>
class UnaryOp
{
public:
Op op;
Input input;
};
template<class Op, class Left, class Right>
class BinaryOp
{
public:
Op op;
Left lhs;
Right rhs;
};
}
Simplify operator overloads: use namespace scoping
If we write the operator overloads in a namespace, they'll only be considered when working with types from that namespace. This means we can avoid having a base class, and we can use unconstrained templates.
namespace expr
{
template<class A>
auto operator-(A const& a)
{
return UnaryOp<Negate, A>{{}, a};
}
template<class A, class B>
auto operator+(A const& a, B const& b)
{
return BinaryOp<Plus, A, B>{{}, a, b};
}
template<class A, class B>
auto operator-(A const& a, B const& b)
{
return BinaryOp<Minus, A, B>{{}, a, b};
}
template<class A, class B>
auto operator*(A const& a, B const& b) {
return BinaryOp<Times, A, B>{{}, a, b};
}
}
Simplify evaluation: Operation types know how to evaluate their inputs
This is pretty simple to achieve - basically, any operation is a functor type that knows how to evaluate the inputs. In C++20, this can be achieved with lambdas, but for our purposes we'll just overload the operator().
namespace expr {
class Negate {
template<class A>
constexpr auto operator()(A&& a) const
noexcept(noexcept(-a))
-> decltype(-a)
{
return -a;
}
};
class Plus {
public:
template<class A, class B>
constexpr auto operator()(A&& a, B&& b) const
noexcept(noexcept(a + b))
-> decltype(a + b)
{
return a + b;
}
};
class Minus {
public:
template<class A, class B>
constexpr auto operator()(A&& a, B&& b) const
noexcept(noexcept(a - b))
-> decltype(a - b)
{
return a - b;
}
};
class Times {
public:
template<class A, class B>
constexpr auto operator()(A&& a, B&& b) const
noexcept(noexcept(a * b))
-> decltype(a * b)
{
return a * b;
}
};
}
Take advantage of pattern-matching with namespace-scope evaluate
Rather than having it as a member function, we can take advantage of pattern matching and recursion when writing an evaluate function at namespace scope.
namespace expr
{
// This one is applied to things that aren't constants or expressions
template<class Thing>
auto evaluate(Thing const& t) -> Thing const& {
return t;
}
template<class Value>
auto evaluate(Constant<Value> const& value) {
return evaluate(value.value);
}
template<class Op, class Input>
auto evaluate(UnaryOp<Op, Input> const& expr) {
return expr.op(evaluate(expr.value));
}
template<class Op, class LHS, class RHS>
auto evaluate(BinaryOp<Op, LHS, RHS> const& expr) {
return expr.op(evaluate(expr.lhs), evaluate(expr.rhs));
}
}
Instead of storing reference here:
template<typename E1, typename E2>
class ExpressionSum : public Expression<ExpressionSum<E1,E2>> {
E1 const& u_; // <------| These are references
E2 const& v_; // <------|
public:
ExpressionSum(E1 const& u, E2 const& v) : u_(u), v_(v)
{ }
// ...
};
These does not cause lifetime extension. The wikipedia article assume the expression template is never stored and only live in the same statement as the expression.
Store them as value:
template<typename E1, typename E2>
class ExpressionSum : public Expression<ExpressionSum<E1,E2>> {
E1 u_; // <------| Fixed!
E2 v_; // <------|
public:
ExpressionSum(E1 const& u, E2 const& v) : u_(u), v_(v)
{ }
// ...
};
You can also extend std::tuple to piggyback on it's EBO:
template<typename E1, typename E2>
class ExpressionSum : public Expression<ExpressionSum<E1,E2>>, private std::tuple<E1, E2> {
auto u_() const -> E1 const& { return std::get<0>(*this); }
auto v_() const -> E2 const& { return std::get<1>(*this); }
public:
ExpressionSum(E1 const& u, E2 const& v) : std::tuple<E1, E2>(u, v)
{ }
// ...
};
Do not use auto with expression templates. If the smart people behind the Eigen library can't make it work, you don't really have a chance either.
I mean, you can copy just about every value involved but that's generally the thing you want to avoid with expression templates.
Related
Consider the following set of classes and the relationship of their operators: We can implement them in two distinct ways. The first where the operators are defined within the class, and the latter where they are defined outside of the class...
template<typename T>
struct A {
T value;
T& operator+(const A<T>& other) { return value + other.value; }
// other operators
};
temlate<typename T>
struct B {
T value;
T& operator+(const B<T>& other) { return value + other.value; }
};
// Or...
template<typename T>
struct A {
T value;
};
template<typename T>
T& operator+(const A<T>& lhs, const A<T>& rhs) { return lhs.value + rhs.value; }
// ... other operators
template<typename T>
struct B {
T value;
};
template<typename T>
T& operator+(const B<T>& lhs, const B<T>& rhs) { return lhs.value + rhs.value; }
// ... other operators
Is there any way in C++ where I would be able to make a single class or struct of operators to where I could simply be able to declare or define them within any arbitrary class C without having to write those same operators multiple times for each class? I'm assuming that the operators will have the same behavior and property for each distinct class that defines them considering that they will all follow the same pattern.
For example:
template<typename T, class Obj>
struct my_operators {
// define them here
};
// Then
template<typename T>
struct A {
T value;
my_operators ops;
};
template<typename T>
struct B {
T value;
my_operators ops;
};
Remember I'm restricting this to C++17 as I'm not able to use any C++20 features such as Concepts... If this is possible, what kind of method or construct would I be able to use, what would its structure and proper syntax look like? If this is possible then I'd be able to write the operators once and just reuse them as long as the pattern of the using classes matches without having to write those operators for each and every individual class...
What about using CRTP inheritance?
#include <iostream>
template <typename T>
struct base_op
{
auto operator+ (T const & o) const
{ return static_cast<T&>(*this).value + o.value; }
};
template<typename T>
struct A : public base_op<A<T>>
{ T value; };
template<typename T>
struct B : public base_op<B<T>>
{ T value; };
int main()
{
A<int> a1, a2;
B<long> b1, b2;
a1.value = 1;
a2.value = 2;
std::cout << a1+a2 << std::endl;
b1.value = 3l;
b2.value = 5l;
std::cout << b1+b2 << std::endl;
}
Obviously this works only for template classes with a value member.
For the "outside the class" version, base_op become
template <typename T>
struct base_op
{
friend auto operator+ (T const & t1, T const & t2)
{ return t1.value + t2.value; }
};
-- EDIT --
The OP asks
now I'm struggling to write their equivalent +=, -=, *=, /= operators within this same context... Any suggestions?
It's a little more complicated because they must return a reference to the derived object... I suppose that (for example) operator+=(), inside base_op, could be something as
T & operator+= (T const & o)
{
static_cast<T&>(*this).value += o.value;
return static_cast<T&>(*this);
}
Taking the answer provided by user max66 using CRTP and borrowing the concept of transparent comparators provided by the user SamVarshavchik within the comment section of my answer, I was able to adopt them and came up with this implementation design:
template<class T>
struct single_member_ops {
friend auto operator+(T const & lhs, T const & rhs)
{ return lhs.value + rhs.value; }
friend auto operator-(T const & lhs, T const & rhs)
{ return lhs.value - rhs.value; }
template<typename U>
friend auto operator+(T const& lhs, const U& rhs)
{ return lhs.value + rhs.value; }
template<typename U>
friend auto operator-(T const& lhs, const U& rhs )
{ return lhs.value - rhs.value;}
};
template<typename T>
struct A : public single_member_ops<A<T>>{
T value;
A() = default;
explicit A(T in) : value{in} {}
explicit A(A<T>& in) : value{in.value} {}
auto& operator=(const T& rhs) { return value = rhs; }
};
template<typename T>
struct B : public single_member_ops<B<T>> {
T value;
B() = default;
explicit B(T in) : value{in} {}
explicit B(B<T>& in) : value{in.value} {}
auto& operator=(const T& rhs) { return value = rhs; }
};
int main() {
A<int> a1(4);
A<int> a2;
A<int> a3{0};
a2 = 6;
a3 = a1 + a2;
B<double> b1(3.4);
B<double> b2(4.5);
auto x = a1 + b2;
auto y1 = a2 - b2;
auto y2 = b2 - a1;
return x;
}
You can see that this will compile found within this example on Compiler Explorer.
The additional templated operator allows for different types: A<T> and B<U> to use the operators even if T and U are different for both A and B provided there is a default conversion between T and U. However, the user will have to be aware of truncation, overflow & underflow, and narrowing conversions depending on their choice of T and U.
Problem Description and Question
I have a template class Class1. It contains in map in which I want to insert structures A or B.
The problem is that the structures A and B have different types of member variables. Structure A has an std::string member variable whereas structure B has an int member variable.
The comparator is based on structure A. So obviously when I want to insert a structure B it will not compile.
Class1<B,B> c2;
c2.AddElement({1},{1});
How can I fix that design Issue? For instance is it possible to keep Class1 as template class and do something to TestCompare?
I also have a constraint. I cannot modify the structures A and B. they are written in C code. I have no right to change them because they are external codes used by other users. I just simplified the code as much as possible.
Source Code
The code was compiled on cpp.sh
#include <iostream>
#include <string>
#include <map>
typedef struct {
std::string a;
} A;
typedef struct {
int b;
} B;
template<typename T1, typename T2> class Class1 {
public :
struct TestCompare {
bool operator()(const T1 & lhs, const T1 & rhs) const {
return lhs.a < rhs.a;
}
};
Class1() {}
~Class1() {}
void AddElement(const T1 & key, const T2 & value) {
m.emplace(key, value);
}
private :
std::map<T1,T2,TestCompare> m;
};
int main()
{
Class1<A,A> c1;
c1.AddElement({"1"},{"1"});
// Problem here. Obviously it will not compile because the Operator is using
// the member variable of struct A.
//Class1<B,B> c2;
//c2.AddElement({1},{1});
//return 0;
}
New Source code
// Example program
#include <iostream>
#include <string>
#include <map>
typedef struct {
std::string a;
} A;
typedef struct {
int b;
} B;
bool operator<(const A & lhs, const A & rhs) {
return lhs.a < rhs.a;
}
bool operator<(const B & lhs, const B & rhs) {
return lhs.b < rhs.b;
}
template<typename T1, typename T2> class Class1 {
public :
Class1() {}
~Class1() {}
void AddElement(const T1 & key, const T2 value) {
m.emplace(key, value);
}
std::map<T1,T2> getMap() {
return m;
}
private :
std::map<T1,T2> m;
};
int main()
{
Class1<A,A> c1;
c1.AddElement({"1"},{"1"});
// Problem here. Obviously it will not compile because the Operator is using
// the member variable of struct A.
Class1<B,B> c2;
c2.AddElement({1},{1});
c2.AddElement({2},{2});
for(const auto &e: c2.getMap()) {
std::cout << e.first.b << " " << e.first.b << std::endl;
}
return 0;
}
I guess you could remove TestCompare from Class1 and template that.
template<typename T> struct TestCompare {
bool operator()(const T & lhs, const T & rhs) const {
// default implementation
return lhs < rhs;
}
};
template<typename T1, typename T2> class Class1 {
...
private :
std::map<T1,T2,TestCompare<T1>> m;
}
You could then specialise TestCompare for A and B
template<> struct TestCompare<A> {
bool operator()(const A & lhs, const A & rhs) const {
return lhs.a < rhs.a;
}
};
template<> struct TestCompare<B> {
bool operator()(const B & lhs, const B & rhs) const {
return lhs.b < rhs.b;
}
};
EDIT:
Actually you could just use std::less instead of TestCompare. It amounts to pretty much the same thing, and std::map uses std::less by default.
TestCompare requires that every type you use must have a member a that can be compared using <. That's a lot of requirements, which implies a terrible design. Add a 3rd template parameter that will be used to pass a function or a functor that compares the objects
struct CompareA {
bool operator()(A const & lhs, A const & rhs) const {
return lhs.a < rhs.a;
}
};
struct CompareB {
bool operator()(B const& lhs, B const& rhs) const {
/*...*/
}
};
template<typename KeyT, typename ValueT, typename Compare> class Dict {
public :
Class1() {}
~Class1() {}
void AddElement(KeyT const & key, ValueT const & value) {
m.emplace(key, value);
}
private :
std::map<KeyT, ValueT, Compare> m;
};
Dict<A, B, CompareA> dictA;
Dict<B, B CompareB> dictB;
You could specialize the struct TestCompare, like john has suggested in his answer, and provide it as the default template argument
template<typename KeyT, typename ValueT, typename Compare = TestCompare<KeyT>> class Dict { /*...*/ };
Such solution will allow you to provide only 2 arguments, like so
Dict<B, B> dict;
while still maintaining the ability to provide another comparer if necessary.
In this minimal example I have a class A with an operator + defined outsise of it:
template<class T> class A {};
template<class T1, class T2> void operator+(A<T1> a, A<T2> b) {}
template<class T> class B : public A<T> {};
int main(int, char**) {
B<int> a, b;
a + b;
return 0;
}
I've tried to create an implicit conversion from B to A but it requires the operator+ to be a friend of A and be defined inside A which will cause problems when more than one instance of A<...> gets instantiated.
So, is there any other way to do this without having to define the operator+ again?
Thank you in advance for any help.
template<class T> class A {
template<class T2>
friend void operator+(A const& lhs, A<T2> const& rhs) {}
};
template<class T> class B : public A<T> {};
int main(int, char**) {
B<int> a, b;
a + b;
return 0;
}
this works. The assymetry in + (one template, one not) ensure that multiple As don't conflict with their +.
In some situations you really need lhs to be an instance of B:
template<class T> struct A {
template<class D, class T2, std::enable_if_t<std::is_base_of<A, D>{}, bool> =true >
friend void operator+(D const& lhs, A<T2> const& rhs) {
std::cout << D::name() << "\n";
}
static std::string name() { return "A"; }
};
template<class T> struct B : public A<T> {
static std::string name() { return "B"; }
};
int main(int, char**) {
B<int> a, b;
a + b;
return 0;
}
which uses A's operator+, but the LHS is of type B. Doing this for B<T2> on the right hand side isn't very viable, it gets ridiculous.
Please consider the following (partial) implementation of a mathematical vector class (which is basically the code you can find in the Wikipedia article about expression templates):
namespace math
{
template<class E>
class vector_expression
{
public:
std::size_t size() const {
return static_cast<E const&>(*this).size();
}
double operator[](size_t i) const
{
if (i >= size())
throw std::length_error("");
return static_cast<E const&>(*this)[i];
}
operator E&() { return static_cast<E&>(*this); }
operator E const&() const { return static_cast<E const&>(*this); }
}; // class vector_expression
template<class E1, class E2>
class vector_sum
: public vector_expression<vector_sum<E1, E2>>
{
public:
vector_sum(vector_expression<E1> const& e1, vector_expression<E2> const& e2)
: m_e1(e1), m_e2(e2)
{
if (e1.size() != e2.size())
throw std::logic_error("");
}
std::size_t size() const {
return m_e1.size(); // == m_e2.size()
}
double operator[](std::size_t i) const {
return m_e1[i] + m_e2[i];
}
private:
E1 const& m_e1;
E2 const& m_e2;
}; // class vector_sum
template<typename E1, typename E2>
vector_sum<E1, E2> operator+(vector_expression<E1> const& e1, vector_expression<E2> const& e2) {
return { e1, e2 };
}
template<typename T>
class vector
: public vector_expression<vector<T>>
{
public:
vector(std::size_t d)
: m_data(d)
{ }
vector(std::initializer_list<T> init)
: m_data(init)
{ }
template<class E>
vector(vector_expression<E> const& expression)
: m_data(expression.size())
{
for (std::size_t i = 0; i < expression.size(); ++i)
m_data[i] = expression[i];
}
std::size_t size() const {
return m_data.size();
}
double operator[](size_t i) const { return m_data[i]; }
double& operator[](size_t i) { return m_data[i]; }
private:
std::vector<T> m_data;
}; // class vector
} // namespace math
How should I extend this implementation to allow the following operations:
vector<double> x = { ... };
auto y = 4711 * x; // or y = x * 4711
auto z = 1 + x; // or x + 1, which should yield z[i] = x[i] + 1
I suppose that I need something like
namespace math
{
template<class E, typename T>
class vector_product
: public vector_expression<vector_product<E, T>>
{
public:
vector_product(vector_expression<E> const& e, T const& t)
: m_e(e), m_t(t)
{ }
std::size_t size() const {
return m_e.size();
}
double operator[](std::size_t i) const {
return m_e[i] * m_t;
}
private:
E const& m_e;
T const& m_t;
}; // class vector_product
template<class E, typename T>
vector_product<E, T> operator*(vector_expression<E> const& e, T const& t) {
return { e, t };
}
template<class E, typename T>
vector_product<E, T> operator*(T const& t, vector_expression<E> const& e) {
return e * t;
}
} // namespace math
but I don't know whether or not this is a good approach. So, how should I do it? And should I add any copy or move constructor/assignment operator? I guess not, since the implicit ones should do a perfect job, cause the only member variable of vector is a STL type.
I would simply extend vector_sum to allow for E2 to be double, and to handle that gracefully if it is. This would involve taking arguments of E1 const& and E2 const& in your constructor, potentially not throwing on size differences (since scalars have no size), and rewriting operator[] to not do indexing. For the last part, something like:
double operator[](std::size_t i) const {
return m_e1[i] + get(m_e2, i);
}
private:
template <class E>
double get(E const& rhs, std::size_t i) const {
return rhs[i];
}
double get(double scalar, std::size_t ) const {
return scalar;
}
This way, if you're adding two vector_expressions, you'll do the indexing, but if you're adding a vector_expression and a double - you won't even try to index into the double. This switch happens at compile-time, so there's no run-time overhead.
Then, all you just need to add a couple more operator+s:
template <typename E1>
vector_sum<E1, double> operator+(vector_expression<E1> const& e1, double d) {
return {e1, d};
}
template <typename E1>
vector_sum<E1, double> operator+(double d, vector_expression<E1> const& e1) {
return {e1, d};
}
Which lets you write:
math::vector<int> x = {1, 2, 3, 4};
math::vector<int> y = {2, 3, 4, 5};
auto sum = 3 + x + 1;
Keeping references to const probably isn't the right thing to do - if you did a+b+c, you'll end up keeping a reference to the temporary a+b. You probably only want to keep a reference to the actual vector, and keep copies of all the intermediate objects.
To support vector_product, you'll probably want vector_sum<E1,E2> to really be vector_binary_op<E1,E2,std::plus<>> and then vector_product<E1,E2> should be vector_binary_op<E1,E2,std::multiplies<>>. That way, you won't have all the duplication.
Inspired by Yakk and Barry, I've finally come up with the following:
namespace math
{
template<class E>
class expression
{
public:
auto size() const {
return static_cast<E const&>(*this).size();
}
auto operator[](std::size_t i) const
{
if (i >= size())
throw std::length_error("");
return static_cast<E const&>(*this)[i];
}
operator E&() { return static_cast<E&>(*this); }
operator E const&() const { return static_cast<E const&>(*this); }
}; // class expression
template<typename T, class Allocator = std::allocator<T>>
class vector
: public expression<vector<T>>
{
private:
using data_type = std::vector<T, Allocator>;
data_type m_data;
public:
using value_type = T;
using allocator_type = Allocator;
using size_type = typename data_type::size_type;
using difference_type = typename data_type::difference_type;
using reference = typename data_type::reference;
using const_reference = typename data_type::const_reference;
using pointer = typename data_type::pointer ;
using const_pointer = typename data_type::const_pointer;
vector(size_type d)
: m_data(d)
{ }
vector(std::initializer_list<value_type> init)
: m_data(init)
{ }
template<class E>
vector(expression<E> const& expression)
: m_data(expression.size())
{
for (size_type i = 0; i < expression.size(); ++i)
m_data[i] = expression[i];
}
size_type size() const {
return m_data.size();
}
value_type operator[](size_type i) const { return m_data[i]; }
value_type& operator[](size_type i) { return m_data[i]; };
}; // class vector
namespace detail
{
template<typename T>
class scalar
: public expression<scalar<T>>
{
public:
using value_type = T;
using allocator_type = std::allocator<void>;
using size_type = typename std::allocator<T>::size_type;
using difference_type = typename std::allocator<T>::difference_type;
using reference = typename std::allocator<T>::reference;
using const_reference = typename std::allocator<T>::const_reference;
using pointer = typename std::allocator<T>::pointer;
using const_pointer = typename std::allocator<T>::const_pointer;
scalar(value_type value)
: m_value(value)
{ }
size_type size() const {
return 0;
}
operator value_type&() { return static_cast<value_type&>(*this); }
operator value_type const&() const { return static_cast<value_type const&>(*this); }
value_type operator[](size_type i) const { return m_value; }
value_type& operator[](size_type i) { return m_value; }
private:
value_type m_value;
}; // class scalar
template<class>
struct is_scalar : std::false_type { };
template<class T>
struct is_scalar<scalar<T>> : std::true_type { };
} // namespace detail
template<class E1, class E2, class BinaryOperation>
class vector_binary_operation
: public expression<vector_binary_operation<E1, E2, BinaryOperation>>
{
public:
using value_type = decltype(BinaryOperation()(typename E1::value_type(), typename E2::value_type()));
using allocator_type = std::conditional_t<
detail::is_scalar<E1>::value,
typename E2::allocator_type::template rebind<value_type>::other,
typename E1::allocator_type::template rebind<value_type>::other>;
private:
using vector_type = vector<value_type, allocator_type>;
public:
using size_type = typename vector_type::size_type;
using difference_type = typename vector_type::difference_type;
using reference = typename vector_type::reference;
using const_reference = typename vector_type::const_reference;
using pointer = typename vector_type::pointer;
using const_pointer = typename vector_type::const_pointer;
vector_binary_operation(expression<E1> const& e1, expression<E2> const& e2, BinaryOperation op)
: m_e1(e1), m_e2(e2),
m_op(op)
{
if (e1.size() > 0 && e2.size() > 0 && !(e1.size() == e2.size()))
throw std::logic_error("");
}
size_type size() const {
return m_e1.size(); // == m_e2.size()
}
value_type operator[](size_type i) const {
return m_op(m_e1[i], m_e2[i]);
}
private:
E1 m_e1;
E2 m_e2;
//E1 const& m_e1;
//E2 const& m_e2;
BinaryOperation m_op;
}; // class vector_binary_operation
template<class E1, class E2>
vector_binary_operation<E1, E2, std::plus<>>
operator+(expression<E1> const& e1, expression<E2> const& e2) {
return{ e1, e2, std::plus<>() };
}
template<class E1, class E2>
vector_binary_operation<E1, E2, std::minus<>>
operator-(expression<E1> const& e1, expression<E2> const& e2) {
return{ e1, e2, std::minus<>() };
}
template<class E1, class E2>
vector_binary_operation<E1, E2, std::multiplies<>>
operator*(expression<E1> const& e1, expression<E2> const& e2) {
return{ e1, e2, std::multiplies<>() };
}
template<class E1, class E2>
vector_binary_operation<E1, E2, std::divides<>>
operator/(expression<E1> const& e1, expression<E2> const& e2) {
return{ e1, e2, std::divides<>() };
}
template<class E, typename T>
vector_binary_operation<E, detail::scalar<T>, std::divides<>>
operator/(expression<E> const& expr, T val) {
return{ expr, detail::scalar<T>(val), std::divides<>() };
}
template<class E, typename T>
vector_binary_operation<E, detail::scalar<T>, std::multiplies<>>
operator*(T val, expression<E> const& expr) {
return{ expr, detail::scalar<T>(val), std::multiplies<>() };
}
template<class E, typename T>
vector_binary_operation<E, detail::scalar<T>, std::multiplies<>>
operator*(expression<E> const& expr, T val) {
return{ expr, detail::scalar<T>(val), std::multiplies<>() };
}
} // namespace math
This allows the operations +, -, /, * for two vectors of the same size as well as the multiplication a * x and x * a of a vector x and a value a. Moreover, we can divide x / a, but not a / x (since that makes no sense). I think that this is the most plausible solution.
However, there are still some issues: I've added an Allocator template parameter to the vector class. In vector_binary_operation I need to know the resulting vector type. It would be possible that both expressions have a different allocator_type. I've decided to choose the allocator_type of the first expression in vector_binary_operation. I don't think that this is a real problem, since I don't think that it would make much sense to use different Allocators in this scenario.
The bigger issue is that I don't know how I need to deal with the expression member variables in vector_binary_operation. It would make sense to declare them as const references, cause the whole point of the code is to avoid unnecessary copies. However, as Barry pointed out, if we write sum = a + b + c, we will end up keeping a reference to the temporary a + b. Doing sum[0] will call operator()[0] on that temporary. But that object was deleted after the previous line.
I don't know what I need to do here and asked another question.
Make a specializattion of template<class E> class vector_expression for E=double.
Add in concept of min/max size (as doubles are 0 to infinite dimension), and maybe is_scalar (might not be needed, except cast-to-scalar?).
Kill vector_sum and take its toys. Make binop_vector that takes a elementop on the elements.
Add
friend binop_vector<vector_expression,R,ElemAdd>
operator+( vector_expression const& l. R const& r ){
return {l,r};
}
friend binop_vector<vector_expression<double>,vector_expression,ElemAdd>
operator+( double const& l. vector_expression const const& r ){
return {l,r};
}
And similar for * with ElemMult to vector_expression. Ths binop uses the element wise operation to implement [].
Change asserts to ensure you have overlapping min/max sizes. Report intersection in binop_vector.
The vector_expression<double> overload has a min 0 max -1 (size_t) and always returns its value. If you need more than one scalar type, write scalar_expression<T> and inherit vector_expression<double> (etc) from it.
The above is not tested, just written on phone as I sit in garage.
template <class T>
class A
{
private:
T m_var;
public:
operator T () const { return m_var; }
........
}
template<class T, class U, class V>
const A<T> operator+ (const U& r_var1, const V& r_var2)
{ return A<T> ( (T)r_var1 + (T)r_var2 ); }
The idea is to overload the + operator once (instead of three) for the cases:
number + A, A + number, A + A (where number is of type T, the same as m_var).
An interesting case would be if m_var is e.g. int and r_var is long.
Any helps would be highly appreciated. Thank you.
The common pattern to achieve what you want is to actually perform it in the opposite direction: provide an implicit conversion from T to the template and only define the operator for the template.
template <typename T>
struct test {
T m_var;
test( T const & t ) : m_var(t) {} // implicit conversion
test& operator+=( T const & rhs ) {
m_var += rhs.m_var;
}
friend test operator+( test lhs, test const & rhs ) { // *
return lhs += rhs;
}
};
// * friend only to allow us to define it inside the class declaration
A couple of details on the idiom: operator+ is declared as friend only to allow us to define a free function inside the class curly braces. This has some advantages when it comes to lookup for the compiler, as it will only consider that operator if either one of the arguments is already a test.
Since the constructor is implicit, a call test<int> a(0); test<int> b = a + 5; will be converted into the equivalent of test<int> b( a + test<int>(5) ); Conversely if you switch to 5 + a.
The operator+ is implemented in terms of operator+=, in a one-liner by taking the first argument by value. If the operator was any more complex this would have the advantage of providing both operators with a single implementation.
The issue with your operator+ is you have 3 template parameters, one for the return type as well as the cast, but there is no way for the compiler to automatically resolve that parameter.
You are also committing a few evils there with casts.
You can take advantage of the that if you define operator+ as a free template function in your namespace it will only have effect for types defined in that namespace.
Within your namespace therefore I will define, using just T and U
template< typename T >
T operator+( const T & t1, const T& t2 )
{
T t( t1 );
t += t2; // defined within T in your namespace
return t;
}
template< typename T, typename U >
T operator+( const T& t, const U& u )
{
return t + T(u);
}
template< typename T, typename U >
T operator+( const U& u, const T& t )
{
return T(u) + t;
}
a + b in general is not covered by this template unless one of the types of a and b is in the namespace where the template was defined.
You should not overload op+ for unrelated types that you know nothing about – this can break perfectly working code that already exists. You should involve your class as at least one of the parameters to the op+ overload.
If you don't want an implicit conversion from T to A<T>, then I would just write out the overloads. This is the clearest code, and isn't long at all, if you follow the "# to #=" overloading pattern:
template<class T>
struct A {
explicit A(T);
A& operator+=(A const &other) {
m_var += other.m_var;
// This could be much longer, but however long it is doesn't change
// the length of the below overloads.
return *this;
}
A& operator+=(T const &other) {
*this += A(other);
return *this;
}
friend A operator+(A a, A const &b) {
a += b;
return a;
}
friend A operator+(A a, T const &b) {
a += A(b);
return a;
}
friend A operator+(T const &a, A b) {
b += A(a);
return b;
}
private:
T m_var;
};
C++0x solution
template <class T>
class A
{
private:
T m_var;
public:
operator T () const { return m_var; }
A(T x): m_var(x){}
};
template<class T,class U, class V>
auto operator+ (const U& r_var1, const V& r_var2) -> decltype(r_var1+r_var2)
{
return (r_var1 + r_var2 );
}
int main(){
A<int> a(5);
a = a+10;
a = 10 + a;
}
Unfortunately changing template<class T,class U, class V> to template<class U, class V> invokes segmentation fault on gcc 4.5.1. I have no idea why?