I have 2 different class templates in the same namespace, xy and rgba. Both of them overload operators == and !=. When I compile, I get the error that the overload has already been defined. Is it because both those classes are in the same namespace? If so, is there a trick to avoid the clash? I tried different labels for the templates, it gave me the same result.
template <typename A>
class xy
{
public:
A x, y;
xy() :x(0), y(0) {}
xy(A x, A y) :x(x), y(y) {}
template<typename B>
xy& operator = (const B& v) {
x = A(v.x), y = A(v.y); return*this;
}
template<typename B, typename C>
friend bool operator == (const B& a, const C& v) {
return a.x == v.x && a.y == v.y;
}
template<typename B, typename C>
friend bool operator != (const B& a, const C& v) {
return a.x != v.x || a.y != v.y;
}
template<typename B>
operator B() const {
return B(x, y);
}
};
class rgba
{
public:
int r, g, b, a;
rgba() :r(255), g(255), b(255), a(255) {}
rgba(int r, int g, int b, int a) :r(r), g(g), b(b), a(a) {}
template<typename B>
rgba& operator = (const B& v) {
r = A(v.r), g = A(v.g), r = A(v.b), a = A(v.a); return*this;
}
template<typename B, typename C>
friend bool operator == (const B& a, const C& v) { // <- already defined ?
return a.r == v.r && a.g == v.g && a.b == v.b && a.a == v.a;
}
template<typename B, typename C>
friend bool operator != (const B& a, const C& v) { // <- already defined ?
return a.r != v.r || a.g != v.g || a.b != v.b || a.a != v.a;
}
template<typename B>
operator B() const {
return B(r, g, b, a);
}
};
There are many good points in the comment, so I will only provide a solution to your compilation problem.
You need some type_traits and if constexpr. If you don't have c++17 you can do it with SFINAE.
#include <iostream>
#include <type_traits>
template <class T, class U = void>
struct has_xy : std::false_type{};
template <class T>
struct has_xy<T, std::void_t<
decltype(std::declval<T>().x),
decltype(std::declval<T>().y)
>
>: std::true_type{};
template <class T, class U = void>
struct has_rgba : std::false_type{};
template <class T>
struct has_rgba<T, std::void_t<
decltype(std::declval<T>().r),
decltype(std::declval<T>().g),
decltype(std::declval<T>().b),
decltype(std::declval<T>().a)
>
>: std::true_type{};
template <typename A>
class xy
{
public:
A x, y;
xy() :x(0), y(0) {}
xy(A x, A y) :x(x), y(y) {}
template<typename B>
xy& operator = (const B& v) {
x = A(v.x), y = A(v.y); return*this;
}
template<typename B, typename C>
friend bool operator == (const B& a, const C& v);
template<typename B, typename C>
friend bool operator != (const B& a, const C& v);
template<typename B>
operator B() const {
return B(x, y);
}
};
class rgba
{
public:
int r, g, b, a;
rgba() :r(255), g(255), b(255), a(255) {}
rgba(int r, int g, int b, int a) :r(r), g(g), b(b), a(a) {}
template<typename B>
rgba& operator = (const B& v) {
r = A(v.r), g = A(v.g), r = A(v.b), a = A(v.a); return*this;
}
template<typename B, typename C>
friend bool operator == (const B& a, const C& v);
template<typename B, typename C>
friend bool operator != (const B& a, const C& v);
template<typename B>
operator B() const {
return B(r, g, b, a);
}
};
template<typename B, typename C>
bool operator == (const B& a, const C& v)
{
if constexpr ( has_xy<B>::value and has_xy<C>::value )
{
return a.x == v.x && a.y == v.y;
}
else if constexpr ( has_rgba<B>::value and has_rgba<C>::value )
{
return a.r == v.r && a.g == v.g && a.b == v.b && a.a == v.a;
}
else
{
return false; // or throw, or don't compile do as you want
}
}
template<typename B, typename C>
bool operator != (const B& a, const C& v)
{
return not operator==(a,v);
}
int main()
{
xy<float> x1,x2;
rgba r1,r2;
if ( x1 == x2 ) { std::cout << " x1 == x2 " << std::endl; }
if ( x1 != x2 ) { std::cout << " x1 != x2 " << std::endl; }
if ( r1 == r2 ) { std::cout << " r1 == r2 " << std::endl; }
if ( r1 != r2 ) { std::cout << " r1 != r2 " << std::endl; }
if ( x1 == r2 ) { std::cout << " x1 == r2 " << std::endl; }
if ( x1 != r2 ) { std::cout << " x1 != r2 " << std::endl; }
}
Live demo : wandbox
Both class are friend with the same function, and the function is only declare once outside of the classes.
Then inside the operator, we select, at compile time, if we can compare en x/y or on r/g/b/a
Related
I'm not sure how to describe when the type of a template is the struct itself as shown below.
template<typename T> struct Point{};
Point<Point<int>> p;
Is that defined behavior? If so, I don't know the best way to implement it so that I can return a common_type without an error as shown below.
#include <iostream>
template<typename T> struct Point
{
Point() {}
template<typename U, typename V> Point(const U& u, const V& v): x(u), y(v) {}
T x,y;
};
template<typename T, typename U>
inline Point<typename std::common_type<T, U>::type> operator+(const Point<T>& p, const U& n)
{
return {p.x+n, p.y+n};
}
int main() {
Point<int> p;
Point<double> r1 = p + 1.5; //works
Point<Point<int>> p2;
Point<Point<double>> r2 = p2 + 1.5; //error
return 0;
}
The error is:
no match for ‘operator+’ (operand types are ‘Point<Point<int> >’ and ‘double’)
If you want this to work (in my opinion it shouldn't, but it's up to you), you can use decltype(std::declval<T>()+std::declval<U>()) instead of std::common_type<...>.
#include <iostream>
template<typename T> struct Point
{
Point(): x{}, y{} {}
template<typename U, typename V> Point(const U& u, const V& v): x(u), y(v) {}
T x,y;
};
template<typename T, typename U>
inline Point<decltype(std::declval<T>()+std::declval<U>())> operator+(const Point<T>& p, const U& n)
{
return {p.x+n, p.y+n};
}
template<typename T>
std::ostream &operator<<(std::ostream& os, const Point<T>& p)
{
os << "Point<" << typeid(T).name() << ">(x=" << p.x << ", y=" << p.y << ")";
return os;
}
int main() {
Point<int> p;
auto r1 = p + 1.5;
Point<Point<int>> p2;
auto r2 = p2 + 1.5;
std::cout << p << "\n";
std::cout << r1 << "\n";
std::cout << p2 << "\n";
std::cout << r2 << "\n";
return 0;
}
I also added an overload the print out a point. Since the C++ standard has no guarantees on what typeid(T).name() will give you might see something different, but this is what I get:
Point<i>(x=0, y=0)
Point<d>(x=1.5, y=1.5)
Point<5PointIiE>(x=Point<i>(x=0, y=0), y=Point<i>(x=0, y=0))
Point<5PointIdE>(x=Point<d>(x=1.5, y=1.5), y=Point<d>(x=1.5, y=1.5))
Point<i> is Point<int>, Point<d> is Point<double>, Point<5PointIiE> is Point<Point<int>>, and Point<5PointIdE> is Point<Point<double>>. Note that I used auto for r1 and r2 so the types are deduced by the compiler.
Again, it's up to you whether you think this behavior makes sense for your class.
This is defined behaviour. To allow usage in a recursive fashion, you'll need to specialize the std::common_type metafunction on your Point class. Note that normally, defining anything within the std namespace results in undefined behaviour - however, the standard specifically allows you to provide specializations for some std templates for custom types.
#include <iostream>
template<typename T> struct Point
{
Point() {}
template<typename U, typename V> Point(const U& u, const V& v): x(u), y(v) {}
T x,y;
};
// provide custom common_type implementation
namespace std {
template <class T, class U>
struct common_type<Point<T>, U> {
using type = Point<typename std::common_type<T, U>::type>;
};
}
template<typename T, typename U>
inline Point<typename std::common_type<T, U>::type> operator+(const Point<T>& p, const U& n) {
return {p.x+n, p.y+n};
}
int main() {
Point<int> p;
Point<double> r1 = p + 1.5; //works
Point<Point<int>> p2;
Point<Point<double>> r2 = p2 + 1.5; //works
Point<Point<Point<int>>> p3;
Point<Point<Point<double>>> r3 = p3 + 1.5; //works
// and so on
return 0;
}
This doesn't work because there is not a common type between Point<anything> and numeric types. You should of course ask yourself whether you want this to work to start with, and whether you simply shouldn't explicitly handle one level of nesting and stop there.
To support arbitrary nesting, what you'd want is a special case for when Point includes another Point inside of it: these are then "peeled away", since the result type for Point<Point<T>, U> is declared as the result of addition of Point<T> and U.
Finally, I imagine that you're assuming that std::common_type<T,U> results in the same type as T{}+U{}, so that the addition via the Point indirection works just like addition on the most interior fields would work. I don't have a problem there as long as you force the compiler to check that fact.
#include <type_traits>
template<typename T> struct Point
{
constexpr Point() {}
template<typename U, typename V> constexpr Point(const U& u, const V& v): x(u), y(v) {}
T x = {}, y = {};
};
template<typename T, typename U>
inline constexpr Point<typename std::common_type_t<T, U>> operator+(const Point<T>& p, const U& n)
{
static_assert(std::is_same_v<std::common_type_t<T,U>, decltype(std::declval<T>() + std::declval<U>())>);
return {p.x+n, p.y+n};
}
template<typename T, typename U>
inline constexpr Point<decltype(Point<T>{} + U{})> operator+(const Point<Point<T>>& p, const U& n)
{
return {p.x+n, p.y+n};
}
template <class T, class U>
inline constexpr bool operator==(const Point<T> &p, const Point<U> &r)
{
return p.x == r.x && p.y == r.y;
}
int main() {
constexpr Point<int> p;
constexpr Point<double> r1 = p + 1.5;
constexpr Point<Point<int>> p2;
constexpr Point<Point<double>> r2 = p2 + 1.5;
constexpr Point<Point<Point<int>>> p3;
constexpr Point<Point<Point<double>>> r3 = p3 + 1.5;
constexpr Point<Point<Point<Point<int>>>> p4;
constexpr Point<Point<Point<Point<double>>>> r4 = p4 + 1.5;
static_assert(r4.x == p4.x + 1.5);
static_assert(r4.x.x == p4.x.x + 1.5);
static_assert(r4.x.x.x == p4.x.x.x + 1.5);
static_assert(r4.x.x.x.x == p4.x.x.x.x + 1.5);
}
I have a templated class A with an inner class B. I want to have a friend == operator. However, the following code does not compile. It says, couldn't deduce template parameter ‘T’
#include <iostream>
template<typename T>
struct A
{
struct B
{
T b;
template<typename T2>
friend bool operator == (const typename A<T2>::B& b1, const typename A<T2>::B& b2);
};
B b;
};
template<typename T>
bool operator == (const typename A<T>::B& b1, const typename A<T>::B& b2)
{
return b1.b == b2.b;
}
int main() {
A<int>::B b1, b2;
b1.b = 3;
b2.b = 2;
std::cout << (b1 == b2) << std::endl;
return 0;
}
I have to have the friend version because the way one of the STL algorithms calls it results in == not found otherwise even if I have bool operator == (const B& b_) { return b == b_.b; }
What is the way to solve this?
It's a non-deduced context.
Ostensibly you could have a definition like
template<typename AB>
bool operator == (const AB& b1, const AB& b2)
{
return b1.b == b2.b;
}
but it's too broad as it catches all types. Yoiy can restrict it this way
template<typename AB>
auto operator == (const AB& b1, const AB& b2) ->
std::enable_if_t<std::is_same_v<AB, typename A<decltype(b1.b)>::B>, bool>
{
return b1.b == b2.b;
}
This worked for me.
#include <iostream>
template<typename T>
struct A
{
struct B
{
T b;
};
friend bool operator==(const typename A<T>::B &b1, const typename A<T>::B &b2)
{
return b1.b == b2.b;
}
B b;
};
int main() {
A<int>::B b1, b2;
b1.b = 3;
b2.b = 2;
std::cout << (b1 == b2) << std::endl;
return 0;
}
I am trying to implement functionality similar to boost/operators.
Here is what I have so far:
template<typename T, typename TAG>
struct strong_type{
explicit strong_type(T v) : v(v){}
T v;
};
template<typename T, typename TAG>
struct addition{
addition() = default;
using N = strong_type<T, TAG>;
friend N operator+(const N &a, const N &b){
return N{ a.v + b.v };
}
};
struct myint_tag{};
struct myint :
strong_type<int, myint_tag>,
addition<int, myint_tag>{
using strong_type<int, myint_tag>::strong_type;
myint(const strong_type &other) : strong_type(v){}
};
int main(){
myint a{ 2 };
myint b{ 3 };
// result is not myint, but strong_type<int, myint_tag>
myint c = a + b;
}
However I don't see how this can be implemented without #define.
Is there a way to implement this without need to write myint(const strong_type &other)?
template<class D>
struct strong_add {
friend D operator+( D lhs, D const& rhs ) {
lhs += rhs; return lhs;
}
friend D& operator+=( D& lhs, D const& rhs ) {
lhs.v += rhs.v;
return lhs;
}
};
struct myint :
strong_type<int, myint_tag>,
strong_add<myint> {
using strong_type<int, myint_tag>::strong_type;
};
Live example.
This uses the CRTP. + takes the lhs argument by value, because if you have cheap-to-move expensive-to-copy types like std::string:
a + b + c + d + e
with a naive const&, const& plus, we get a copy every +, as we create a brand new object at each return point from the operator.
With a value, const& plus, first a is copied. Then we do += b, then move the result, then += c then move the result, then += e then move the result. Only one copy is made.
We can go further if you want.
First we do this:
template<class T>
class detect_strong_type {
template<class X, class Tag>
static std::true_type tester( strong_type<X, Tag>const* );
static std::false_type tester( void* );
public:
using type=decltype( tester( (T*)nullptr ) );
};
template<class T>
using is_strong_type = typename detect_strong_type<T>::type;
enum class operators {
add, subtract, multiply, divide
};
template<operators o>
using op_tag_t = std::integral_constant<operators, o>;
template<operators o>
constexpr op_tag_t<o> op_tag{};
auto default_op( op_tag_t<operators::add> ) { return [](auto& lhs, auto const& rhs){ lhs += rhs; }; }
auto default_op( op_tag_t<operators::subtract> ) { return [](auto& lhs, auto const& rhs){ lhs -= rhs; }; }
auto default_op( op_tag_t<operators::multiply> ) { return [](auto& lhs, auto const& rhs){ lhs *= rhs; }; }
auto default_op( op_tag_t<operators::divide> ) { return [](auto& lhs, auto const& rhs){ lhs /= rhs; }; }
template<operators op, class D, class...Skip>
void do_operator( op_tag_t<op>, D& lhs, D const& rhs, Skip&&... ) {
default_op( op_tag<op> )( lhs, rhs );
}
template<class D>
struct can_add {
friend D operator+( D lhs, D const& rhs ) {
lhs += rhs; return lhs;
}
friend D& operator+=( D& lhs, D const& rhs ) {
do_operator( op_tag<operators::add>, lhs, rhs );
return lhs;
}
};
template<class D>
struct can_subtract {
friend D operator-( D lhs, D const& rhs ) {
lhs -= rhs; return lhs;
}
friend D& operator-=( D& lhs, D const& rhs ) {
do_operator( op_tag<operators::subtract>, lhs, rhs );
return lhs;
}
};
template<class D>
struct can_multiply {
friend D operator*( D lhs, D const& rhs ) {
lhs *= rhs; return lhs;
}
friend D& operator*=( D& lhs, D const& rhs ) {
do_operator( op_tag<operators::multiply>, lhs, rhs );
return lhs;
}
};
template<class D>
struct can_divide {
friend D operator/( D lhs, D const& rhs ) {
lhs *= rhs; return lhs;
}
friend D& operator/=( D& lhs, D const& rhs ) {
do_operator( op_tag<operators::divide>, lhs, rhs );
return lhs;
}
};
template<class D>
struct can_math:
can_add<D>, can_multiply<D>, can_subtract<D>, can_divide<D>
{};
now we teach do_operator about strong_type:
template<operators op, class D,
std::enable_if_t< is_strong_type<D>{}, bool> =true
>
void do_operator( op_tag_t<op>, D& lhs, D const& rhs ) {
do_operator( op_tag<op>, lhs.v, rhs.v );
}
and this works:
struct myint :
strong_type<int, myint_tag>,
can_math<myint>
{
using strong_type<int, myint_tag>::strong_type;
};
int main(){
myint a{ 2 };
myint b{ 3 };
myint c = a*b + b - a;
}
Live example
Now this is a bit overkill just for strong operators. What it does let you do is:
struct some_type: can_add<some_type> {
std::vector<int> values;
friend void do_operator( op_tag_t<operators::add>, some_type& lhs, some_type const& rhs ) {
lhs.values.insert( lhs.values.end(), rhs.values.begin(), rhs.values.end() );
}
};
and now some_type + some_type and some_type += some_type are implemented.
Yakk - Adam Nevraumont is definitely very good,
but I found much easier and clear way to do the same.
If you think a bit, all these addition / strong_add classes, need to inject "global" operators.
The operators itself do not need to be friend, but friend keyword is used, because else the operator will be not injected into "global" space.
Also no one needs the struct. It is used only to inject the operators, so it might be empty struct.
template<typename T, typename TAG>
struct strong_type{
using type = T;
T v;
explicit constexpr strong_type(const T &v) : v(v) {}
};
Then surprisingly:
template<class V>
struct arithmetic{
friend constexpr V operator+ (const V &a, const V &b){ return { a.v + b.v }; }
friend constexpr V operator- (const V &a, const V &b){ return { a.v - b.v }; }
friend constexpr V operator* (const V &a, const V &b){ return { a.v * b.v }; }
friend constexpr V operator/ (const V &a, const V &b){ return { a.v / b.v }; }
friend V &operator+=(V &a, const V &b){ a.v += b.v; return a; }
friend V &operator-=(V &a, const V &b){ a.v -= b.v; return a; }
friend V &operator*=(V &a, const V &b){ a.v *= b.v; return a; }
friend V &operator/=(V &a, const V &b){ a.v /= b.v; return a; }
};
and finally:
struct myint_tag{};
struct myint : strong_type<int, myint_tag>,
arithmetic <myint>
{
using strong_type::strong_type;
};
int main(){
constexpr myint a{ 2 };
constexpr myint b{ 3 };
myint x{ 3 };
myint y{ 5 };
x = x + y * x;
}
i am currently working on a c++ project and now i am stuck already for a while. It's about delayed evaluation with expression templates and (for me at least) a strange bad_alloc.
If you try the code below, you'll notice runtime error bad_alloc due to the very last addition b+c. So thats the point where the delayed evaluation is done. Furthermore the code below compiles and runs fine if you remove the references of the members of "Expression" (left,right). But i need references there, due to performance, etc. . However i also dont see, why i cant use references there.
I've already spent a lot of time with it. Please let me know if somebody can help me.
Best Regards.
#include <iostream>
#include <vector>
template<typename value_t, typename left_t, typename right_t, typename op_t>
class Expression
{
public:
typedef value_t value_type;
explicit Expression(const left_t &left,
const right_t &right,
const op_t &op) :
left(left),
right(right),
op(op)
{
}
value_t operator [](const size_t &i) const
{
return op(left[i],right[i]);
}
size_t size() const { return left.size();}
private:
const left_t &left;
const right_t &right;
//const left_t left;
//const right_t right;
const op_t &op;
};
template<class left_t,
class right_t,
class value_t = typename left_t::value_type,
class op_t = std::plus<value_t>>
const Expression<value_t, left_t, right_t, op_t> operator +(const left_t &left,
const right_t &right)
{
return Expression<value_t,left_t,right_t,op_t>(left, right, op_t());
}
template<typename value_t, typename data_t = std::vector<value_t>>
class Vector : public data_t
{
public:
typedef value_t value_type;
using data_t::size;
Vector(const std::initializer_list<value_t> &list) :
data_t(list)
{
}
Vector(const size_t &n) :
data_t(n)
{
}
Vector(const Vector &v) :
data_t(v)
{
}
template<typename left_t, typename right_t, typename op_t>
Vector(const Expression<value_t,left_t,right_t,op_t> &v) :
data_t(v.size())
{
operator =(v);
}
template<typename vec_t>
Vector(const vec_t &v) :
data_t(v.size())
{
operator =(v);
}
template<typename vec_t>
Vector &operator =(const vec_t &v)
{
for(size_t i = 0; i < data_t::size(); ++i)
data_t::operator [](i) = v[i];
return (*this);
}
friend std::ostream &operator <<(std::ostream &os, const Vector &v)
{
if(v.size())
os << v[0];
for(size_t i = 1; i < v.size(); ++i)
os << " " << v[i];
return os;
}
};
int main()
{
Vector<double> a{0,1,2};
auto b = a+a+a;
auto c = a;
std::cout << a+a+a+a << std::endl;
std::cout << b+c << std::endl; // gives bad_alloc
return 0;
}
"But i need references there, due to performance, etc."
Prove it.
In expression templates, all¹ the information should be compile-time.
You can see my example here for a simple expression template:
// we have lazy placeholder types:
template <int N> struct placeholder {};
placeholder<1> _1;
placeholder<2> _2;
placeholder<3> _3;
// note that every type here is stateless, and acts just like a more
// complicated placeholder.
// We can have expressions, like binary addition:
template <typename L, typename R> struct addition { };
template <typename L, typename R> struct multiplication { };
// here is the "factory" for our expression template:
template <typename L, typename R> addition<L,R> operator+(L const&, R const&) { return {}; }
template <typename L, typename R> multiplication<L,R> operator*(L const&, R const&) { return {}; }
///////////////////////////////////////////////
// To evaluate/interpret the expressions, we have to define "evaluation" for each type of placeholder:
template <typename Ctx, int N>
auto eval(Ctx& ctx, placeholder<N>) { return ctx.arg(N); }
template <typename Ctx, typename L, typename R>
auto eval(Ctx& ctx, addition<L, R>) { return eval(ctx, L{}) + eval(ctx, R{}); }
template <typename Ctx, typename L, typename R>
auto eval(Ctx& ctx, multiplication<L, R>) { return eval(ctx, L{}) * eval(ctx, R{}); }
///////////////////////////////////////////////
// A simple real-life context would contain the arguments:
#include <vector>
struct Context {
std::vector<double> _args;
// define the operation to get an argument from this context:
double arg(int i) const { return _args.at(i-1); }
};
#include <iostream>
int main() {
auto foo = _1 + _2 + _3;
Context ctx { { 3, 10, -4 } };
std::cout << "foo: " << eval(ctx, foo) << "\n";
std::cout << "_1 + _2 * _3: " << eval(ctx, _1 + _2 * _3) << "\n";
}
So what you need is a literal type that holds a reference to the associated value, and defer all other evaluation to evaluation time.
I might prefer to add the size() operation as a free function, so that you don't have to encumber all the expression types with it (Separation Of Concerns).
¹ nearly, nl. except when encoding literals
Proof Of Concept
Using the strategy outlined:
Live On Coliru
#include <iostream>
#include <tuple>
namespace ETL {
template <typename T>
struct Literal {
T value;
T get() const { return value; }
};
/*
*template <typename T>
* static inline std::ostream& operator<<(std::ostream& os, ETL::Literal<T> const& lit) {
* return os << __PRETTY_FUNCTION__ << "\n actual: lit.value = " << lit.value;
* }
*/
template <class L, class R, class Op>
struct BinaryExpr : std::tuple<L, R, Op> { // tuple optimizes for empty element types
BinaryExpr(L l, R r, Op op)
: std::tuple<L, R, Op> { l, r, op }
{}
L const& get_lhs() const { return std::get<0>(*this); }
R const& get_rhs() const { return std::get<1>(*this); }
Op const& get_op() const { return std::get<2>(*this); }
};
template <class L, class R, class Op> auto cured(BinaryExpr<L,R,Op> _) { return _; }
template <class T> auto cured(Literal<T> l) { return std::move(l); }
template <class T> Literal<T> cured(T&& v) { return {std::forward<T>(v)}; }
template <class Op, class L, class R>
BinaryExpr<L, R, Op> make_binexpr(L&& l, R&& r) { return { std::forward<L>(l), std::forward<R>(r), Op{} }; }
template <class L, class R> auto operator +(L&& l, R&& r)
{ return make_binexpr<std::plus<>>(cured(std::forward<L>(l)), cured(std::forward<R>(r))); }
template <class L, class R> auto operator -(L&& l, R&& r)
{ return make_binexpr<std::minus<>>(cured(std::forward<L>(l)), cured(std::forward<R>(r))); }
template <class L, class R> auto operator *(L&& l, R&& r)
{ return make_binexpr<std::multiplies<>>(cured(std::forward<L>(l)), cured(std::forward<R>(r))); }
template <class L, class R> auto operator /(L&& l, R&& r)
{ return make_binexpr<std::divides<>>(cured(std::forward<L>(l)), cured(std::forward<R>(r))); }
template <class L, class R> auto operator %(L&& l, R&& r)
{ return make_binexpr<std::modulus<>>(cured(std::forward<L>(l)), std::forward<R>(r)); }
template <typename T> auto val(T const& v)
{ return cured(v); }
namespace impl {
template <class T>
static constexpr auto is_indexable(T const&) -> decltype(std::declval<T const&>()[0], std::true_type{}) { return {}; }
static constexpr auto is_indexable(...) -> decltype(std::false_type{}) { return {}; }
struct {
template <class T> size_t operator()(T const& v) const { return (*this)(v, is_indexable(v)); }
template <class T> size_t operator()(T const& v, std::true_type) const { return v.size(); }
template <class T> size_t operator()(T const&, std::false_type) const { return 0; }
template <class T> size_t operator()(Literal<T> const& l) const { return (*this)(l.value); }
template <class L, class R, class Op>
size_t operator()(BinaryExpr<L,R,Op> const& be) const { return (*this)(be.get_lhs()); }
} size;
struct {
template <class T>
auto operator()(size_t i, T const& v) const { return (*this)(i, v, is_indexable(v)); }
template <class T>
auto operator()(size_t i, T const& v, std::true_type) const { return v[i]; }
template <class T>
auto operator()(size_t, T const& v, std::false_type) const { return v; }
template <class T> auto operator()(size_t i, Literal<T> const& l) const { return (*this)(i, l.value); }
template <class L, class R, class Op>
auto operator()(size_t i, BinaryExpr<L,R,Op> const& be) const {
return be.get_op()((*this)(i, be.get_lhs()), (*this)(i, be.get_rhs()));
}
} eval_at;
}
template <typename T> size_t size(T const& v) { return impl::size(v); }
template <typename T> auto eval_at(size_t i, T const& v) { return impl::eval_at(i, v); }
}
#include <vector>
template <class value_t>
struct Vector : std::vector<value_t> {
using data_t = std::vector<value_t>;
typedef value_t value_type;
using data_t::data_t;
template <typename Expr>
Vector(Expr const& expr) { *this = expr; }
template <typename Expr>
Vector& operator=(Expr const& expr) {
this->resize(size(expr));
for (size_t i = 0; i < this->size(); ++i)
this->at(i) = eval_at(i, expr);
return *this;
}
friend std::ostream &operator<<(std::ostream &os, const Vector &v) {
for (auto& el : v) os << " " << el;
return os;
}
};
int main() {
Vector<double> a { 1, 2, 3 };
using ETL::operator+;
using ETL::operator*;
//std::cout << typeid(a + a * 4 / 2).name() << "\n";
#define DD(x) std::cout << typeid(x).name() << " size: " << ETL::size(x) << " result:" << (x) << "\n"
DD(a * -100.0);
auto b = a + a + a;
auto c = a;
std::cout << size(b) << "\n";
std::cout << (a + a + a + a) << "\n";
std::cout << a * 4.0 << "\n";
std::cout << b + c << "\n";
std::cout << (a + a + a + a) - 4 * a << "\n";
}
Prints
ETL::BinaryExpr<ETL::Literal<Vector<double>&>, ETL::Literal<double>, std::multiplies<void> > size: 3 result: -100 -200 -300
3
4 8 12
4 8 12
4 8 12
0 0 0
Consider this C++ code:
struct A {
A operator*(A a) { return A(); } // A*A -> A
};
struct B {
A operator*(B b) { return A(); } // B*B -> A
};
int main() {
A t2 = B()*B() * A(); // works
A t1 = A() * B()*B(); // errors
return 0;
}
A*B is not allowed, but B*B is. Do languages exist which will choose their operator precedence rules based on the types of the variables?
It turns out that my original question can in fact be expressed in C++:
struct A {
friend A operator*(A const& a1, A const &a2) { return A(); } // A*A -> A
};
struct B {
friend A operator*(B const& a, B const &b) { return A(); } // B*B -> A
};
template<typename A, typename B>
struct unresolved_multiply {
A const& a;
B const& b;
};
template<typename A, typename B>
unresolved_multiply<A, B> operator*(A const& a, B const &b) {
return {a, b};
}
template<typename A, typename B, typename C>
auto operator*(unresolved_multiply<A, B> const& ab, C const &c) {
return ab.a * (ab.b * c);
}
int main() {
A t1 = B()*B() * A(); // works
A t2 = A() * B()*B(); // works
A t3 = (A() * B())*B(); // works...
return 0;
}
Of course, disregarding parentheses like this is a terrible idea.