Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Dot operator Overloading

216 views
Skip to first unread message

Thiago A.

unread,
Mar 31, 2010, 6:36:41 PM3/31/10
to

Using function notation we have expressions like that:

F(G(H(X)))

Where we have to read from inside to outside. It is not so bad; this
is the normal mathematical notation.

However in some cases, I would prefer to read expression from left to
right applying the next function in the previous result.

H(x) G() F()

We can have this kind of expressions using member functions.
x.H().G().H();

In this case, each return type must have member functions for that
algorithm. But algorithms can me separated from data.
The solution that I could think about is to have an overload for
operator dot.

ReturnType operator . H (const X& x)
{
}

This operator dot would be valid only if the expression x.H() was not
defined before using member functions.

The syntax sugar would be useful in some cases.

For instance, we could write:

matrix.Rotate(pi).ReflectHor().Inverse();

instead of:

Inverse(ReflectHor(Rotate(matrix)));

Is this too much syntax sugar?
Is the dot operator overload too hard to be defined? (I know that we
can’t do this today)


Also in C++ we don’t have this kind of syntax sugar:
using x {
.Rotate(pi);
.RefelectHor();
.Inverse();
}


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Seungbeom Kim

unread,
Mar 31, 2010, 7:51:18 PM3/31/10
to
On 2010-03-31 15:36, Thiago A. wrote:
>
> However in some cases, I would prefer to read expression from left to
> right applying the next function in the previous result.
>
> H(x) G() F()

Only in the (special) case where each function takes one parameter.

Expressions could be made readable from left to right in general, for
example, with a notation that puts the function name on the right:

(w, ((x, y)f1, z)f2)f3

instead of

f3(w, f2(f1(x, y), z))

but this deviates too much from the conventional mathematical notation.

Furthermore, if an expression becomes too long and complicated
to parse at a glance, it's always a good idea to break it up
into smaller expressions and give them proper names.

> The syntax sugar would be useful in some cases.
>
> For instance, we could write:
>
> matrix.Rotate(pi).ReflectHor().Inverse();
>
> instead of:
>
> Inverse(ReflectHor(Rotate(matrix)));

This is already possible, for example, if matrix is an object of
a class that has member functions Rotate, ReflectHor, and Inverse,
and they all return the reference to *this.

This is not much different from the operator chaining:

std::cout << std::setw(8) << value << std::endl

where each function returns the reference to the stream.

> Also in C++ we don’t have this kind of syntax sugar:
> using x {
> .Rotate(pi);
> .RefelectHor();
> .Inverse();
> }

Because it can easily be written out:

x.Rotate(pi);
x.ReflectHor();
x.Inverse();

If x turns out to be a long expression you don't want to repeat,
you can create an alias:

T& x = long_expression_with(arguments);
x.Rotate(pi);
x.ReflectHor();
x.Inverse();

where T could be just written as "auto" in the next version of C++.

--
Seungbeom Kim

pfultz2

unread,
Mar 31, 2010, 10:36:04 PM3/31/10
to

> For instance, we could write:
>
> matrix.Rotate(pi).ReflectHor().Inverse();
>
> instead of:
>
> Inverse(ReflectHor(Rotate(matrix)));

You don't need to overload the dot operator to get this kind of
behaviour. You just have each function return another matrix or a
reference to a matrix like so:

class Matrix
{
public:
Matrix& Rotate(double val)
{
//Do calculations
return *this;
}

Matrix& ReflectHor()
{
//Do calculations
return *this;
}

Matrix& Inverse()
{
//Do calculations
return *this;
}
};

You could return a copy instead of reference if you have an immutable
matrix class. But now you can use your class like so:

Matrix m;
m.Rotate(3.14).ReflectHor().Inverse();

Andy Venikov

unread,
Mar 31, 2010, 10:36:20 PM3/31/10
to
Thiago A. wrote:
> Using function notation we have expressions like that:
>
> F(G(H(X)))
>
> Where we have to read from inside to outside. It is not so bad; this
> is the normal mathematical notation.
>
> However in some cases, I would prefer to read expression from left to
> right applying the next function in the previous result.
>
> H(x) G() F()
>
> We can have this kind of expressions using member functions.
> x.H().G().H();
>
> In this case, each return type must have member functions for that
> algorithm. But algorithms can me separated from data.
> The solution that I could think about is to have an overload for
> operator dot.
>
> ReturnType operator . H (const X& x)
> {
> }
>
> This operator dot would be valid only if the expression x.H() was not
> defined before using member functions.
>
> The syntax sugar would be useful in some cases.
>
> For instance, we could write:
>
> matrix.Rotate(pi).ReflectHor().Inverse();
>
> instead of:
>
> Inverse(ReflectHor(Rotate(matrix)));


You can't overload operator . ()
But in your case you don't need to.
Just have every algorithm function (like Rotate())
return a reference to a (non-local) object that defines other needed
functions, like ReflectHor()


HTH,
Andy.

Thiago A.

unread,
Mar 31, 2010, 11:04:29 PM3/31/10
to
> > However in some cases, I would prefer to read expression from left to
> > right applying the next function in the previous result.
>
> > H(x) G() F()
>
> Only in the (special) case where each function takes one parameter.
>
> Expressions could be made readable from left to right in general, for
> example, with a notation that puts the function name on the right:
>
> (w, ((x, y)f1, z)f2)f3
>
> instead of
>
> f3(w, f2(f1(x, y), z))
>
> but this deviates too much from the conventional mathematical notation.


The first argument is always the one we can put before the dot, and we
could have N arguments.

For instance, using your sample:

f3(w, f2(f1(x, y), z))

and changing names just to express the idea:

f3_DotProduct(w_vector, f2_Normalize( f1_Rotate(x_vector,
y_scalar), z_scalar))

could be written:

vector_w.f3_DotProduct(vector_x.f1_Rotate(scalar_y).f2_Normalize(scalar_z));

--

Mathias Gaunard

unread,
Apr 1, 2010, 10:08:43 AM4/1/10
to
On Mar 31, 11:36 pm, "Thiago A." <thiago.ad...@gmail.com> wrote:
> Using function notation we have expressions like that:
>
> F(G(H(X)))
>
> [...]

>
> For instance, we could write:
>
> matrix.Rotate(pi).ReflectHor().Inverse();

This works fine, but the main problem is that it is intrusive: you
need to have such functions declared as members of the returned types.
Using operator overloading, you can avoid this issue.

Consider matrix | Rotate(pi) | ReflectHor() | Inverse()
Such syntax is used by the new extension to Boost.Range.

Thiago A.

unread,
Apr 1, 2010, 1:16:41 PM4/1/10
to

> But in your case you don't need to.
> Just have every algorithm function (like Rotate())
> return a reference to a (non-local) object that defines other needed
> functions, like ReflectHor()

The problem of member-function is to repeat the same algorithm in each
class, or to have to create a non-member function and call it from
each member-function. Consequently each class must know each
algorithm. Apart of that, sometimes you cannot change the class code,
like std::vector or the type is not a class.

For instance:
2.pow(3);

int operator . pow (int a, int b) { return pow(a, b); }

Thiago A.

unread,
Apr 1, 2010, 1:17:18 PM4/1/10
to

> > Using function notation we have expressions like that:
>
> > F(G(H(X)))
>
> > [...]
>
> > For instance, we could write:
>
> > matrix.Rotate(pi).ReflectHor().Inverse();
>
> This works fine, but the main problem is that it is intrusive: you
> need to have such functions declared as members of the returned types.
> Using operator overloading, you can avoid this issue.
>
> Consider matrix | Rotate(pi) | ReflectHor() | Inverse()
> Such syntax is used by the new extension to Boost.Range.

I agree.
In the matrix sample, the operator * could be used.
But considering generic samples the math operators can be VERY
confusing.
Maybe the general idea is to create �named� operators like "pow".
2 + 2 pow 3;
However in this case we should also declare the precedence and this
also would be very confusing.

I think that the dot operator would be simpler because the precedence
is the same we use in class members.
2 + 2.pow(3)

achp

unread,
Apr 1, 2010, 1:17:27 PM4/1/10
to
On Apr 1, 2:36 am, "Thiago A." <thiago.ad...@gmail.com> wrote:
> However in some cases, I would prefer to read expression from left to
> right applying the next function in the previous result.
>
> H(x) G() F()

I don't think you can have it that way.

However, you might choose to have it this way:

apply(x, H, G, F);

where

template<class T>
T apply(T x)
{
return std::forward<T>(x);
}

template<class T, class F1, class... Fn>
auto apply(T x, F1 f1, Fn... fn) -> decltype(apply(f1(x),
std::forward<Fn>(fn)...))
{
return apply(f1(x), std::forward<Fn>(fn)...);
}

Sorry for mistakes, I have not tested it, but you probably got the
idea.

Seungbeom Kim

unread,
Apr 1, 2010, 5:54:40 PM4/1/10
to
On 2010-04-01 07:08, Mathias Gaunard wrote:
> On Mar 31, 11:36 pm, "Thiago A."<thiago.ad...@gmail.com> wrote:
>>
>> For instance, we could write:
>>
>> matrix.Rotate(pi).ReflectHor().Inverse();
>
> This works fine, but the main problem is that it is intrusive: you
> need to have such functions declared as members of the returned types.
> Using operator overloading, you can avoid this issue.

This is a valid concern. Then how about defining one function
that can call arbitrary operations:

matrix.apply(Rotate, pi).apply(ReflectHor).apply(Inverse);

> Consider matrix | Rotate(pi) | ReflectHor() | Inverse()
> Such syntax is used by the new extension to Boost.Range.

I like this syntax, too!

--
Seungbeom Kim

Kumar Anurag

unread,
Apr 2, 2010, 11:58:52 AM4/2/10
to
On Apr 2, 2:54 am, Seungbeom Kim <musip...@bawi.org> wrote:
> On 2010-04-01 07:08, Mathias Gaunard wrote:
>
> > On Mar 31, 11:36 pm, "Thiago A."<thiago.ad...@gmail.com> wrote:
>
> >> For instance, we could write:
>
> >> matrix.Rotate(pi).ReflectHor().Inverse();
>
> > This works fine, but the main problem is that it is intrusive: you
> > need to have such functions declared as members of the returned types.
> > Using operator overloading, you can avoid this issue.
>
> This is a valid concern. Then how about defining one function
> that can call arbitrary operations:
>
> matrix.apply(Rotate, pi).apply(ReflectHor).apply(Inverse);
>
> > Consider matrix | Rotate(pi) | ReflectHor() | Inverse()
> > Such syntax is used by the new extension to Boost.Range.
>
> I like this syntax, too!

{ edits: quoted sig & banner removed. please keep readers in mind when you
quote. -mod }

I have read that .(dot) operator can't be overloaded. Is it false??


--

Mark Zaytsev

unread,
Apr 9, 2010, 5:16:22 PM4/9/10
to
On Mar 31, 6:36 pm, "Thiago A." <thiago.ad...@gmail.com> wrote:
> Using function notation we have expressions like that:
>
> F(G(H(X)))
>
> Where we have to read from inside to outside. It is not so bad; this
> is the normal mathematical notation.
>
> However in some cases, I would prefer to read expression from left to
> right applying the next function in the previous result.
>
> H(x) G() F()

Why not to overload 'operator ,' ?
x, H(), G(), F()

0 new messages