Topic: Left hand operators vs right hand operators


Author: "Greg Herlihy" <greghe@pacbell.net>
Date: Thu, 12 Oct 2006 10:40:42 CST
Raw View

On Oct 9, 10:17 pm, Kaba <n...@here.com> wrote:
> But, alas, defining the function as a free template function does not
> work, as shown, so in general the only option is to both declare and
> define the function inside the class definition as a friend?

No, it's not the only option - but it is the most convenient. As I
noted, the program could implement the overloaded plus operator (with
the parameters reversed) for A<int> itself. The drawback of course is
that because the overloaded operator when implemented as a function
template will not implicitly convert its parameters, the program would
have to implement the plus operator as an ordinary, non-template
function - and remember to do so for all other A specializations.

Declaring the overloaded plus operator a friend of the A class template
essentially writes an overloaded plus operator for each A
specialization and does so automatically; Because even though the
friend operator plus overload is parameterized by type, it is not a
template function - which is why it works and the original function
template version does not. More information about templates and their
friends can be found in    14.5.3 of the latest draft C++ Standard.

> If this is true, then I'd like to raise two things of interest:
> 1) There are functions that must be *defined* inside the class
> definition to work as desired.

I believe that is the case. Still, it seems a small price to pay to
accomplish the impossible.

> 2) To find a way to express the left-hand binary operator this way might
> require a lot of years (for me, 12). This is too much for such a trivial
> desire!

I'm not sure that I understand. You've been wanting this support for
the last 12 years? I'll admit the  use of a friend function in a class
template is one of the more obscure corners of the C++ language (I
think it even has a special name which I don't recall). In fact I had
almost forgotten that declaring a friend function in this way was even
possible. And to be honest, I wasn't even sure that it would work until
I tried it.

Greg


---
[ 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                      ]





Author: none@here.com (Kaba)
Date: Fri, 13 Oct 2006 15:24:12 GMT
Raw View
> No, it's not the only option - but it is the most convenient. As I
> noted, the program could implement the overloaded plus operator (with
> the parameters reversed) for A<int> itself. The drawback of course is
> that because the overloaded operator when implemented as a function
> template will not implicitly convert its parameters, the program would
> have to implement the plus operator as an ordinary, non-template
> function - and remember to do so for all other A specializations.

Yep, that is a solution, while not automatic. This would be a problem=20
with 3rd party code, which you are not allowed to touch: you can't then=20
add the specializations for new types. And, of course, that would also=20
be a bit inelegant and cause some maintenance burden.

> Declaring the overloaded plus operator a friend of the A class template
> essentially writes an overloaded plus operator for each A
> specialization and does so automatically; Because even though the
> friend operator plus overload is parameterized by type, it is not a
> template function - which is why it works and the original function
> template version does not. More information about templates and their
> friends can be found in =A714.5.3 of the latest draft C++ Standard.

Understood.

> > If this is true, then I'd like to raise two things of interest:
> > 1) There are functions that must be *defined* inside the class
> > definition to work as desired.
>=20
> I believe that is the case. Still, it seems a small price to pay to
> accomplish the impossible.

Agreed. Other than the nonuniformity of this functionality, the=20
important thing here is, in my opinion, the difficulty of finding this=20
way. I don't remember having read about this particular problem of left-
hand operators anywhere, nor having seeing a solution, until now (has=20
anyone else?).

Especially the following conventions easily hide finding this solution:
- it is good style to write class function definitions separate from=20
class definition.
- in general, it is good to avoid 'friends', while they do have their=20
uses.
- friends are mostly associated dealing with class access, not=20
generation of code.

> I'm not sure that I understand. You've been wanting this support for
> the last 12 years? I'll admit the  use of a friend function in a class
> template is one of the more obscure corners of the C++ language (I
> think it even has a special name which I don't recall). In fact I had
> almost forgotten that declaring a friend function in this way was even
> possible. And to be honest, I wasn't even sure that it would work until
> I tried it.

Well, I don't remember how many years I have wanted it, but the thing=20
is, if it was even a one year, that would be too much for a single=20
answer (for such a 'simple' problem).

The question is a simple "how do I make left-hand operators work exactly=20
like the right-hand operators?", and that makes even experts stumble.=20
Making the solution easier would certainly fit many of the desired=20
properties of the wanted proposals for the next standard.

And of course, there is still the nonuniformity that for style reasons=20
I'd want to write the function definition out of the class definition,=20
but the language does not allow it (in the general case).

--=20
Kalle Rutanen
http://kaba.hilvi.org

---
[ 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                      ]





Author: giecrilj@stegny.2a.pl ("Kri tof elechovski")
Date: Mon, 16 Oct 2006 17:09:32 GMT
Raw View
I came up with the same solution myself after reading the relevant sectio=
ns=20
of the standard.
However, my solution failed because my target was placement new operator.
=3D(;-O (crying aloud and stomping my feet)
Chris

Uzytkownik "Kaba" <none@here.com> napisal w wiadomosci=20
news:MPG.1f99da5bcabdbbf49897c6@news.cc.tut.fi...
> No, it's not the only option - but it is the most convenient. As I
> noted, the program could implement the overloaded plus operator (with
> the parameters reversed) for A<int> itself. The drawback of course is
> that because the overloaded operator when implemented as a function
> template will not implicitly convert its parameters, the program would
> have to implement the plus operator as an ordinary, non-template
> function - and remember to do so for all other A specializations.

Yep, that is a solution, while not automatic. This would be a problem
with 3rd party code, which you are not allowed to touch: you can't then
add the specializations for new types. And, of course, that would also
be a bit inelegant and cause some maintenance burden.

> Declaring the overloaded plus operator a friend of the A class template
> essentially writes an overloaded plus operator for each A
> specialization and does so automatically; Because even though the
> friend operator plus overload is parameterized by type, it is not a
> template function - which is why it works and the original function
> template version does not. More information about templates and their
> friends can be found in =A714.5.3 of the latest draft C++ Standard.

Understood.

> > If this is true, then I'd like to raise two things of interest:
> > 1) There are functions that must be *defined* inside the class
> > definition to work as desired.
>
> I believe that is the case. Still, it seems a small price to pay to
> accomplish the impossible.

Agreed. Other than the nonuniformity of this functionality, the
important thing here is, in my opinion, the difficulty of finding this
way. I don't remember having read about this particular problem of left-
hand operators anywhere, nor having seeing a solution, until now (has
anyone else?).

Especially the following conventions easily hide finding this solution:
- it is good style to write class function definitions separate from
class definition.
- in general, it is good to avoid 'friends', while they do have their
uses.
- friends are mostly associated dealing with class access, not
generation of code.

> I'm not sure that I understand. You've been wanting this support for
> the last 12 years? I'll admit the  use of a friend function in a class
> template is one of the more obscure corners of the C++ language (I
> think it even has a special name which I don't recall). In fact I had
> almost forgotten that declaring a friend function in this way was even
> possible. And to be honest, I wasn't even sure that it would work until
> I tried it.

Well, I don't remember how many years I have wanted it, but the thing
is, if it was even a one year, that would be too much for a single
answer (for such a 'simple' problem).

The question is a simple "how do I make left-hand operators work exactly
like the right-hand operators?", and that makes even experts stumble.
Making the solution easier would certainly fit many of the desired
properties of the wanted proposals for the next standard.

And of course, there is still the nonuniformity that for style reasons
I'd want to write the function definition out of the class definition,
but the language does not allow it (in the general case).

--=20
Kalle Rutanen
http://kaba.hilvi.org

---
[ 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                      ]


---
[ 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                      ]





Author: Kaba <none@here.com>
Date: Sun, 8 Oct 2006 12:29:23 CST
Raw View
Consider we have a template class A templated over a type Type. We
create a member operator+(const Type& value). Then we create an non-
member template operator+(const Type& value, const A<Type>& a) to take
care of the left hand side summation. These two functions have a big
difference: the former is a normal member function, while the latter is
a template function. What this means is that implicit conversions are
not available for calling the templated function.

For example, consider:

(1) A<float> a;
(2) a = a + 1;
(3) a = 1 + a;

While (2) is ok, (3) does not compile, because of the template function
omitting the implicit conversion int->float.

Now, one can simulate the implicit conversion by giving the template
function extra freedoms over the left hand parameter type, call it Type2
and then explicitly computing it inside the template function (Type
converted = value;). Then the left-hand operator+ works like the right-
hand operator+, except that the former creates code-bloat. The need for
simulation also seems a bit like reverse-engineering.

One can see that there clearly is a non-symmetry between the left-hand
and right-hand operators. Now, my question is, whether there has been
any considerations on solving this non-symmetry?

In a nutshell, the goal would be to make it easy to describe a left-hand
operator+ (or any other binary operator) that works equivalently to a
member right-hand operator+ with templates (with non-template classes
that is naturally the case).

A first-thought that solves this problem is:
- you could get the wanted behaviour by creating a mechanism to restrict
the template deduction mechanism: no deduction for 'const Type& value',
just for 'const A<Type>& a'. In effect, using a flag to denote 'used in
deduction' or 'not used in deduction' for each parameter. A default
would be that all parameters are used in the deduction.

Illustrating code of the words above:

template <typename Type>
class A
{
public:
   A<Type> operator+(
      const Type& that) const
   {
      A<Type> result;
      // Do something
      return result;
   }
};

template <typename Type>
A<Type> operator+(const Type& that,
                  const A<Type>& vec)
{
   A<Type> result;
   // Do something
   return result;
}

template <typename Type, typename Type2>
A<Type> operator-(const Type2& that,
                  const A<Type>& vec)
{
   A<Type> result;
   // Do something
   return result;
}

template <typename Type, typename Type2>
A<Type> operator*(const Type2& that,
                  const A<Type>& vec)
{
   // Simulate implicit conversion in the
   // function call.
   Type2 converted = that;

   A<Type> result;
   // Do something using 'converted'
   return result;
}

template <typename Type>
A<Type> operator/(no_decuction: const Type& that,
                  const A<Type>& vec)
{
   A<Type> result;
   // Do something.
   return result;
}


int main()
{
   A<float> a;

   // Ok, normal member function, implicit conversion to float
   a = a + 1;

   // Does not compile. Not matched by the function template.
   //a = 1 + a;

   // Ok. Matches, but produces code bloat and possibly
   // wrong behavior.
   a = 1 - a;

   // Ok. Matches, but produces code bloat. The simulated
   // conversion also seems a bit lame.
   a = 1 * a;

   // Ok. Just a first-thought, not part of C++.
   a = 1 / a;

   return 0;
}

--
Kalle Rutanen
http://kaba.hilvi.org

---
[ 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                      ]





Author: AlbertoBarbati@libero.it (Alberto Ganesh Barbati)
Date: Sun, 8 Oct 2006 22:41:58 GMT
Raw View
Kaba ha scritto:
> Consider we have a template class A templated over a type Type. We=20
> create a member operator+(const Type& value). Then we create an non-
> member template operator+(const Type& value, const A<Type>& a) to take=20
> care of the left hand side summation. These two functions have a big=20
> difference: the former is a normal member function, while the latter is=
=20
> a template function. What this means is that implicit conversions are=20
> not available for calling the templated function.
>=20
> For example, consider:
>=20
> (1) A<float> a;
> (2) a =3D a + 1;=20
> (3) a =3D 1 + a;
>=20
> While (2) is ok, (3) does not compile, because of the template function=
=20
> omitting the implicit conversion int->float.
>=20
> Now, one can simulate the implicit conversion by giving the template=20
> function extra freedoms over the left hand parameter type, call it Type=
2=20
> and then explicitly computing it inside the template function (Type=20
> converted =3D value;). Then the left-hand operator+ works like the righ=
t-
> hand operator+, except that the former creates code-bloat. The need for=
=20
> simulation also seems a bit like reverse-engineering.

Code bloat? Assuming your "addition" operations is commutative, you can=20
define your left-hand operator as:

template <typename T1, typename T2>
inline A<Type> operator+(const T1& that, const A<T2>& vec)
{
    return vec.operator+(that);
}

Given a reasonable optimizing compiler, you won't get any code bloat=20
from that. Just compile and look at the assembler output. (Notice that=20
the conversion moved from the caller to the callee, but it doesn't=20
matter, you have to make the conversion anyway).

> One can see that there clearly is a non-symmetry between the left-hand=20
> and right-hand operators. Now, my question is, whether there has been=20
> any considerations on solving this non-symmetry?

Good design rules (at least good according to some people, including=20
myself) says that a binary operator is best implemented through free=20
functions. So I would not use one member function + one free function,=20
but two free functions. Voil=E0: asymmetry disappeared.

Just my opinion,

Ganesh

---
[ 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                      ]





Author: none@here.com (Kaba)
Date: Mon, 9 Oct 2006 16:17:09 GMT
Raw View
> Code bloat? Assuming your "addition" operations is commutative, you can=
=20
> define your left-hand operator as:
>=20
> template <typename T1, typename T2>
> inline A<Type> operator+(const T1& that, const A<T2>& vec)
> {
>     return vec.operator+(that);
> }
>=20
> Given a reasonable optimizing compiler, you won't get any code bloat=20
> from that. Just compile and look at the assembler output. (Notice that=20
> the conversion moved from the caller to the callee, but it doesn't=20
> matter, you have to make the conversion anyway).

Because we are talking about the general case, not always is the=20
addition commutative and despite of the possible optimizations, the=20
different instantiations are not the same function. So yes, in general=20
there is code bloat. Logically, there should be no need for code bloat=20
to start with.

> Good design rules (at least good according to some people, including=20
> myself) says that a binary operator is best implemented through free=20
> functions. So I would not use one member function + one free function,=20
> but two free functions. Voil=E0: asymmetry disappeared.

Asymmetry disappeared, but in the wrong direction. You generalized the=20
more specific normal function member operator+ to get equally working=20
binary operators: now they are both templates and thus they lose the=20
possibility of using implicit conversions in the calling process. While=20
implicit conversions are bad in general, with native types they are most=20
convenient (int->float etc).

The problem in this case is that to be able to express your intent, you=20
have to over-generalize (by introducing another template parameter, and=20
then doing the implicit conversion explicitly).

As another point of view, consider that you are not able to express=20
something with template classes that is possible with normal classes=20
(similarly working lh- and rh-binary operators w.r.t some type, one=20
function per handedness per class)

--=20
Kalle Rutanen
http://kaba.hilvi.org

---
[ 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                      ]





Author: "Greg Herlihy" <greghe@pacbell.net>
Date: Mon, 9 Oct 2006 11:20:18 CST
Raw View

On Oct 8, 3:41 pm, AlbertoBarb...@libero.it (Alberto Ganesh Barbati)
wrote:
> Kaba ha scritto:
> > One can see that there clearly is a non-symmetry between the left-hand
> > and right-hand operators. Now, my question is, whether there has been
> > any considerations on solving this non-symmetry?
> Good design rules (at least good according to some people, including
> myself) says that a binary operator is best implemented through free
> functions. So I would not use one member function + one free function,
> but two free functions. Voil   : asymmetry disappeared.

The asymmetry being described is not due to the difference between
member and a free function implementations of a binary plus operator.
In fact there really is little difference between the two. Because any
global function whose first parameter is a class object can be
reformulated as a class method. To effect the transformation, the first
parameter is is replaced by an implicit "this" pointer to its value. So
in essence, an overloaded binary plus operator class method is simply a
more convenient package for the free function version of the same
routine.

The asymmetry that the sample progam aims to demonstrate is that a
member function that overloads the binary plus operator means that "a +
1" becoms a valid expression. A matching routine of the same operator
that has the two parameter types reversed, however, appears not to not
attain the equivalent result: "1 +a", for example, remains an invalid
expression.

The program itself produces this asymmetry by mistakenly implementing a
free function operator+() in a way that does not match the member
operator+(). The free operator+() function after all is a function
template - whereas the member function operator+() is not a member
function template. Granted, the member operator+() belongs to a class
template - but once the class template A<int> is instantiated, the
operator+ method itself is not further parameterized. So the assymmetry
the program demonstrates simply derives from comparing a non-template
function with a function template - so naturally their behaviors
differ.

It is possible to declare an operator+() with the parameters reversed
that does match the member function. To do so, let's first examine the
member operator+() as if it were implemented as a free function:

      A<int> operator+(const A<int>& lhs, int rhs);

So a corresponding version of this function that reversed the two
parameter types would look like this:

     A<int> operator+( int lhs, const A<int>& rhs);

So the program just needs a function with the above declaration to
attain the symmetrical behavior for the binary plus operator for all
A<int> objects.

So far so good. Now, there is a practical obstacle that must be
overcome. When the template class A<int> is instantiated, its member
operator()+ is ready to be instantiated for A<int> objects just as soon
as it is needed. So its matching operator+ needs to have this same,
on-demand availability for A<int> objects (and for any other A template
class objects). How can that by done? The answer is by implementing the
second operator+() routine as a friend of the class template<A>:

    template <typename Type>
    class A
    {
    public:
        A operator+(const Type& that) const
        {
            A result;
            // Do something
            return result;
        }

        friend A operator+(const Type& lhs, const A& rhs)
        {
            A result;
            // Do something
            return result;

            // or if commutative
            return rhs + lhs;
        }
    };

With the friend operator+() function now implemented, the goal of
symmetry for the overloaded binary plus operator has been realized::

    A<int> a

    1 + a; // OK
    a + 1; // OK

Greg


---
[ 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                      ]





Author: Kaba <none@here.com>
Date: Mon, 9 Oct 2006 23:17:49 CST
Raw View
> With the friend operator+() function now implemented, the goal of
> symmetry for the overloaded binary plus operator has been realized::

Impressive! It works just like required. I wouldn't have thought it to
be possible. What in the world is the interpretation? First of all, the
friend function as declared cannot be part of the class, so it must mean
a function outside the class. Furthermore, no class is specified, so it
must mean a free function. Then again, the function is parametrized by
the enclosing class, which means the function must be itself templated.

But, alas, defining the function as a free template function does not
work, as shown, so in general the only option is to both declare and
define the function inside the class definition as a friend?

If this is true, then I'd like to raise two things of interest:
1) There are functions that must be *defined* inside the class
definition to work as desired.
2) To find a way to express the left-hand binary operator this way might
require a lot of years (for me, 12). This is too much for such a trivial
desire!

--
Kalle Rutanen
http://kaba.hilvi.org

---
[ 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                      ]





Author: "Kri tof elechovski" <giecrilj@stegny.2a.pl>
Date: Tue, 10 Oct 2006 12:16:37 CST
Raw View
Uzytkownik "Kaba" <none@here.com> napisal w wiadomosci
news:MPG.1f950d9f2d9ffcc09897c2@news.cc.tut.fi...
>> With the friend operator+() function now implemented, the goal of
>> symmetry for the overloaded binary plus operator has been realized::
>
> Impressive! It works just like required. I wouldn't have thought it to
> be possible. What in the world is the interpretation? First of all, the
> friend function as declared cannot be part of the class, so it must mean
> a function outside the class. Furthermore, no class is specified, so it
> must mean a free function. Then again, the function is parametrized by
> the enclosing class, which means the function must be itself templated.
>
> But, alas, defining the function as a free template function does not
> work, as shown, so in general the only option is to both declare and
> define the function inside the class definition as a friend?
>
> If this is true, then I'd like to raise two things of interest:
> 1) There are functions that must be *defined* inside the class
> definition to work as desired.
> 2) To find a way to express the left-hand binary operator this way might
> require a lot of years (for me, 12). This is too much for such a trivial
> desire!
>

In general, if you want the compiler to instantiate an ordinary function
along with the class template it works upon,
you can define it as a friend.
However, there is one exception: placement operator new(size_t, A<T> &).
If you define it as a friend to A<T>,
you can call it explicitly to return void *, but the new expression
new(A<T>) T does not work.
Sad but true, C++ lacks support for custom object pools.
When we have an intermediate allocator class,
it would be tempting to use placement new to create new objects;
but there is no context-free placement delete and there is no way to
generate the operator new in a generic way.
It is a very strange phenomenon
that such a basic and intuitive feature as operator new and delete has to be
deprecated and wrapped in allocators.
I think it is because operator new came long before the templates
and therefore the two do not work well together.

Chris


---
[ 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                      ]