Re: "auto" operators syntax
Andrei Polushin wrote:
Hi,
Most user-defined operators should have well-known semantics, so they
are implemented by pattern, written so many times by many people.
It might be quite annoying for experts, and error-prone for novices.
I propose to allow operators with "auto" modifier, so that compiler is
able to provide predefined implementation for them.
It looks like the following:
class A {
int first;
int second;
public:
A(int first, int second) auto;
A& operator+=(const A& a) auto;
static A operator+(const A& a, const A& b) auto;
A& operator++() auto;
A operator++(int) auto;
bool operator==(const A& a) const auto;
bool operator!=(const A& a) const auto;
bool operator<(const A& a) const auto;
bool operator>(const A& a) const auto;
bool operator>=(const A& a) const auto;
bool operator<=(const A& a) const auto;
};
The code to be generated is shown in /*...*/ comments:
class A {
int first;
int second;
public:
A(int first, int second) auto;
/*
: first(first), second(second) {}
*/
A& operator+=(const A& a) auto;
/*
first += a.first;
second += a.second;
return *this;
*/
static A operator+(const A& a, const A& b) auto;
/*
A tmp = a;
tmp += b;
return tmp;
*/
A& operator++() auto;
/*
++first;
++second;
return *this;
*/
A operator++(int) auto;
/*
A tmp = *this;
operator++();
return tmp;
*/
bool operator==(const A& a) const auto;
/*
return first == a.first
&& second == a.second
;
*/
bool operator!=(const A& a) const auto;
/*
return !(*this == a);
*/
bool operator<(const A& a) const auto;
/*
return first < a.first
|| first == a.first && second < a.second
;
*/
bool operator>(const A& a) const auto;
/*
return a < *this;
*/
bool operator>=(const A& a) const auto;
/*
return !(*this < a);
*/
bool operator<=(const A& a) const auto;
/*
return !(*this > a);
*/
};
In addition, the same syntax should be allowed for main():
int main() auto;
/*
Do what I mean
*/
I don't see what you mean for main.
Anyway, most of these "auto" operators are meaningless, and would never
be useful.
A much more useful system would be the automatic implementation of some
operators in terms of others.
For instance, += in terms of + and =, or + in terms of
copy-construction and +=.
Often, we get a class of objects on which an order relation is defined
: < <= == != >= > must be defined; usually in terms of a single cmp()
function which returns a negative, zero or positive value.
But, IMHO it doesn't require a core language change:
The strangely recurring template patterns used on a few well-chosen
template base classes would work well and have much more meaningful
semantics:
#include <iostream>
template <class T>
class Comparable {
int cmp(const T& other) const {
return static_cast<const T&>(*this).cmp(other);
}
public:
bool operator <(const T& other) const {
return cmp(other)<0;
}
bool operator <=(const T& other) const {
return cmp(other)<=0;
}
bool operator ==(const T& other) const {
return cmp(other)==0;
}
bool operator !=(const T& other) const {
return cmp(other)!=0;
}
bool operator >=(const T& other) const {
return cmp(other)>=0;
}
bool operator >(const T& other) const {
return cmp(other)>0;
}
};
template <class T,class Scalar>
/* base requirement : T must support a T::add(Scalar) method.
Rationale : T::add is a better choice than T::operator+=, because it
gives the freedom to redefine operator+= (and other operators),
independently in the T class.
Scalar must have a constructor supporting 1 as argument.
*/
class Incrementable {
T& that() {return static_cast<T&>(*this);}
const T& that() const {return static_cast<const T&>(*this);}
public:
T operator +(Scalar value) const {
T new_object(that());
new_object.add(value);
return new_object;
}
T& operator +=(Scalar value) {
that().add(value);
return that();
}
T& operator ++() {
that().add(Scalar(1));
return that();
}
T operator ++(int) {
T new_object(that());
++that();
return new_object;
}
};
/* you can also write a Decrementable class implementing - -= and --
and a class combining Incrementable and Decrementable features.
*/
/* You can also write an Additionable class for classes which support
self addition (i.e. T& operator+(const T&,const T&) ), in that case ++
will not be defined.
*/
class Value:public Comparable<Value>,public Incrementable<Value,int> {
public:
int cmp(const Value& other) const {return value-other.value;}
void add(int incr) { /* Note : It could even be a virtual function !
*/
value+=incr;
}
int value;
Value(int val):value(val) {}
};
int main() {
Value v0(42),v1(53);
std::cout << (v0<v1) << (v0<=v1) << (v0==v1) << (v0!=v1) << (v0>=v1)
<< (v0>v1) << "\n";
v0++;
v0+=10;
std::cout << (v0<v1) << (v0<=v1) << (v0==v1) << (v0!=v1) << (v0>=v1)
<< (v0>v1) << "\n";
std::cout << ((++v0)+3).value << "\n";
}
Of course, you're free to define your own operator set classes.
In real life ... The same operators can have different semantics on
different classes.
Addition is a good example.
Sometimes, a class can be added a scalar (e.g. random iterators).
But, sometimes the class can be added to itself (e.g. matrixes).
In that case, there should be a template base class for "matrix-style
semantics", and another for "scalar addition semantic", more like the
example given above.
With your proposal, we must stick with a single "universal semantic",
that would impose a single view on the proper semantics of operators.
IMHO, it would be a better idea to write once all these useful template
base classes, and perhaps add them to the boost library... Or, if they
reveal to be very useful, there could be a Technical Report containing
them.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]