C++ Logo

std-discussion

Advanced search

Re: commutative functions

From: Andrew Schepler <aschepler_at_[hidden]>
Date: Tue, 1 Sep 2020 18:07:59 -0400
>
>
> Am 01.09.2020 um 02:51 schrieb Jefferson Carpenter via Std-Discussion:
> > On 8/30/2020 12:35 AM, Jeremy Ong via Std-Discussion wrote:
> >> Perhaps a type trait is a suitable alternative for this (as opposed to
> >> introducing yet another keyword that decorates a function signature).
> >> For
> >> example: https://godbolt.org/z/ecaro9
> >
>

This operator+ has "too perfect forwarding" and can break many other
operators. Here it breaks an ordinary std::string concatenation:

https://godbolt.org/z/bvnd9f


> > +1
> >
> > Also use std::enable_if instead of a static_assert in the function
> > body. This takes advantage of SFINAE so that you can write
> > `does_commute` for some types, `does_anticommute` for other types, etc.
> >
> > https://godbolt.org/z/7ssTjW
> >
>

This isn't quite right. The "template <..., std::enable_if_t<...>>" does
take the template out of overload resolution when the enable_if expression
is false. But when the expression is true, it declares a non-type template
parameter with type void, which is not allowed. The godbolt link doesn't
notice the issue because check_commute now calls the original specific
operator+(complex, float), not the template. One simple fix: Make the type
int instead, and add a default template argument, as in "template <...,
std::enable_if_t<..., int> = 0>". Demo: https://godbolt.org/z/hseceW

I also added std::forward for perfect forwarding. This might help for an
underlying function like X operator+(X x, const Y& y) { x += y; return x; }


> >>
> >> On Sat, Aug 29, 2020 at 6:03 PM Tobias W. via Std-Discussion <
> >> std-discussion_at_[hidden]> wrote:
> >>
> >>> Hi everyone!
> >>> I am coming with a mathematical and embedded systems background and
> >>> there is something that bothered me since I started programming.
> >>> Firstly, I would like to explain the problem with an example that
> >>> occurs
> >>> often in slightly different but similar ways.
> >>> Secondly, I will propose a possible solution.
> >>> Of course, I would love to hear your thoughts.
> >>> So let's imagine we have three classes.
> >>> class Real{
> >>> double value;
> >>> ...
> >>>
> >>> };
> >>>
> >>> class Imag{
> >>> double value;
> >>> ...
> >>>
> >>> }
> >>>
> >>> class Complex{
> >>> double Im;
> >>> double Re;
> >>> ...
> >>>
> >>> };
> >>>
> >>> Then if I was to write functions, for example, an add function or
> >>> operator+ and would like to add them in all possible permutations of
> >>> arguments.
> >>> This means, that I would have to write all those following functions,
> >>> just to add them:
> >>>
> >>> Complex operator+ (Real a, Imag b);
> >>> Complex operator+ (Imag b, Real a); // Mirrored function
> >>> Complex operator+ (Real a, Complex b);
> >>> Complex operator+ ( Complex b, Real a); //Mirrored function
> >>> Complex operator+ (Imag a, Complex b);
> >>> Complex operator+ ( Complex b, Imag a); //Mirrored function
> >>>
> >>> From a mathematical point of view, all mirrored functions are
> >>> obsolete,
> >>> because the intent has already been stated in the function above.
> >>> Furthermore, each mirrored function will use additional program memory
> >>> and this can become an issue on embedded devices.
> >>> Therefore I would like to introduce the idea of the expression
> >>> "commutative" that can be added to functions.
> >>> The commutative expression will tell the compiler that he can switch
> >>> the
> >>> parameters that will be passed to a function.
> >>> Following this, the keyword will only be allowed for functions that
> >>> possess two and only two parameters.
> >>> Then the example from above may look something like this:
> >>>
> >>> commutative Complex operator+ (Real a, Imag b);
> >>> commutative Complex operator+ (Real a, Complex b);
> >>> commutative Complex operator+ (Imag a, Complex b);
> >>>
> >>> If the compiler comes across a statement like below, he is allowed to
> >>> restructure the code.
> >>>
> >>> Complex = Complex * Imag + Real;
> >>>
> >>> will change to:
> >>>
> >>> Complex = Real + Imag * Complex;
> >>>
> >>> and now the three commutative functions can be used to process this
> >>> command.
> >>>
> >>> This is far less to write, the intent is clear, fewer functions are
> >>> used
> >>> and I only pay for the programme memory I need.
> >>> This, of course, is not allowed to change the evaluation order of
> >>> operator* and operator+. The operator* will always be evaluated first
> >>> and the operator+ will always work with its result.
> >>> The operator+ is not allowed to steal one of the operator* 's
> >>> parameters.
> >>> commutative will, of course, be available for all functions that take 2
> >>> parameters, not just operators.
> >>>
> >>> These are my thoughts, as already stated above, I would love to hear
> >>> yours.
> >>>
> >>> Sincerely,
> >>> Tobias Wallner
> >>>
> >>> --
> >>> Std-Discussion mailing list
> >>> Std-Discussion_at_[hidden]
> >>> https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion
> >>>
> >>
> >>
> --
> Std-Discussion mailing list
> Std-Discussion_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion
>

Received on 2020-09-01 17:11:41