Topic: Why's of C++ -- Part 5 (implied member functions discussion)


Author: postmast.root.admi.gov@iname.com (blargg)
Date: 1999/08/25
Raw View
In article <37C1D871.3C81E23D@tribble.com>, David R Tribble
<david@tribble.com> wrote:

> Greg Brewer wrote:
> >
> > Whenever a class is defined, there is always an implied copy
> > constructor.  I guess one could say that there is also an implied
> > void constructor if none are defined but I think that's a stretch.
> > In cases when I use a class to define my own datatype with a full
> > complement of logical operators, I've wonder why it is always
> > necessary to define all the logical operators.  If a class defines
> > operators for less than, greater than and equal without defining
> > greater than or equal, less than or equal, and not equal, whe
> > couldn't the compiler imply the missing logical operators.
>
> How about:
>
>     #include <utility>
>     using namespace std::rel_ops;
>
> See 20.2 in the standard.

What about it? Please show how it would work (yes, this is a trick
question - but try anyway).


[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: David R Tribble <david@tribble.com>
Date: 1999/08/26
Raw View
blargg wrote:
>
> In article <37C1D871.3C81E23D@tribble.com>, David R Tribble
> <david@tribble.com> wrote:
>
> > Greg Brewer wrote:
> > >
> > > Whenever a class is defined, there is always an implied copy
> > > constructor.  I guess one could say that there is also an implied
> > > void constructor if none are defined but I think that's a stretch.
> > > In cases when I use a class to define my own datatype with a full
> > > complement of logical operators, I've wonder why it is always
> > > necessary to define all the logical operators.  If a class defines
> > > operators for less than, greater than and equal without defining
> > > greater than or equal, less than or equal, and not equal, whe
> > > couldn't the compiler imply the missing logical operators.
> >
> > How about:
> >
> >     #include <utility>
> >     using namespace std::rel_ops;
> >
> > See 20.2 in the standard.
>
> What about it? Please show how it would work (yes, this is a trick
> question - but try anyway).

I'm not expert, but here goes:

    class Mine
    {
        ...
    public:
        bool    operator <(const Mine &r) const;
        bool    operator ==(const Mine &r) const;
    };

    #include <utility>
    using namespace std::rel_ops;

    void foo()
    {
        Mine    a;
        Mine    b;

        if (a < b) ...;    // Calls Mine::operator<()

        if (a == b) ...;   // Calls Mine::operator==()

        if (a <= b) ...;   // Uses std::rel_ops::operator<=(),
                           // which calls Mine::operator<()

        if (a != b) ...;   // Uses std::rel_ops::operator!=(),
                           // which calls Mine::operator==()
    }

Am I missing something?  Is it more complicated to use than that?

-- David R. Tribble, david@tribble.com --
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: "Greg Brewer" <nospam.gregb@hal-pc.org>
Date: 1999/08/24
Raw View
Tero Pulkkinen <terop@modeemi.cs.tut.fi> wrote in message
news:86btbyh50h.fsf@c156c.w2.ton.tut.fi...
> "Greg Brewer" <nospam.gregb@hal-pc.org> writes:
> This can be done automatically pretty easily.
>
> template<class T>
> class Comparisions {
> public:
>    friend bool operator>(const T&,const T&) const;
>    friend bool operator==(const T&,const T&) const;
>    friend bool operator<(const T&,const T&) const;
>    ...
> };

This is true; however, accomplishing the same thing another way isn't what I
had in mind.  I was intrigued by blargg's idea though.  On problem I have
with this is that it would not be a standard way to perform the task.  I do
have my own library of functions and headers for performing frequent
operations; however, I have run into problems with this approach to short
comings I see in C++.  First, I use a number of 3rd party libraries.  When I
have a problem where a 3rd party library function appears to not operate
correctly, the vender will want a small sample of code demonstrating the
problem.  It starts getting trouble some when I have to send in the section
of code plus all my private stuff.  No one wants to wade through it; nor do
I really blame them.

> This should not be made automatic - Sometimes its useful to have
> non-standard behaviors, which does not obey operator>= equals !operator<.

I think you missed something.  The implicite operator I described would only
be used if an operator is not explicitely given.  If function for operator>=
is defined then the !operaor< is not used.  This is another area were I see
a problem with the work-around given.  I expect that warnings would be
generated if I wished to prove an explicit operator for only one of the
implicit logical operations.  I have not tested this to be certain; however,
I think my compiler would issues a warning when my function hides a base
class function.

Greg Brewer
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: David R Tribble <david@tribble.com>
Date: 1999/08/24
Raw View
Greg Brewer wrote:
>
> Whenever a class is defined, there is always an implied copy
> constructor.  I guess one could say that there is also an implied
> void constructor if none are defined but I think that's a stretch.
> In cases when I use a class to define my own datatype with a full
> complement of logical operators, I've wonder why it is always
> necessary to define all the logical operators.  If a class defines
> operators for less than, greater than and equal without defining
> greater than or equal, less than or equal, and not equal, whe
> couldn't the compiler imply the missing logical operators.

How about:

    #include <utility>
    using namespace std::rel_ops;

See 20.2 in the standard.

-- David R. Tribble, david@tribble.com --
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: "Greg Brewer" <nospam.gregb@hal-pc.org>
Date: 1999/08/23
Raw View
Whenever a class is defined, there is always an implied copy constructor.  I
guess one could say that there is also an implied void constructor if none
are defined but I think that's a stretch.  In cases when I use a class to
define my own datatype with a full complement of logical operators, I've
wonder why it is always necessary to define all the logical operators.  If a
class defines operators for less than, greater than and equal without
defining greater than or equal, less than or equal, and not equal, whe
couldn't the compiler imply the missing logical operators.

Consider, the follow
class X {
public:
   // ...
   bool operator >(const X&) const;
   bool operator ==(const X&) const;
   bool operator <(const X&) const;
};
.....
   if (x1 >= x2) foo();

when processing the comparison, the compiler will see that there is no
definition for >= so instead of generating an error message, let it try
!x1.operator <(x2).

This is more of a why not instead of a why.  Doesn't really solve any
problems.  Just an idea I thought I would throw out for discussion.

Greg Brewer
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: postmast.root.admi.gov@iname.com (blargg)
Date: 1999/08/23
Raw View
In article <7pk7sk$1064$1@news.hal-pc.org>, "Greg Brewer"
<nospam.gregb@hal-pc.org> wrote:

> Whenever a class is defined, there is always an implied copy constructor.  I
> guess one could say that there is also an implied void constructor if none
> are defined but I think that's a stretch.  In cases when I use a class to
> define my own datatype with a full complement of logical operators, I've
> wonder why it is always necessary to define all the logical operators.

Because it's your data type and you need to tell everyone what they mean for it.

> If a
> class defines operators for less than, greater than and equal without
> defining greater than or equal, less than or equal, and not equal, why
> couldn't the compiler imply the missing logical operators.

Because the operators only have a predefined meaning when applied to
built-in types. For user-defined types, the language doesn't promote or
enforce any correspondence or similarity to the built-in operator.

The main reason the default default ctor, copy ctor, copy assignment, and
operator & operators are generated (or implied) for a user-defined type is
for C compatibility.

If you want this correspondence, it can be provided conveniently with
templates. One way is the B&N trick:

    template<class T>
    struct rel_ops_base {

        friend bool operator > ( T const& x, T const& y ) {
            return y < x;
        }

        friend bool operator != ( T const& x, T const& y ) {
            return !(x == y);
        }

        // etc.
    };

// user

    struct X : rel_ops_base<X> {
        friend bool operator < ( X const&, X const& ) {
            // ...
        }

        friend bool operator == ( X const&, X const& ) {
            // ...
        }
    };

Adding the ": rel_ops_base<X>" is quite concise.

Another I came up with, which I haven't heard much comment on, is using a
namespace (relying on Koenig lookup):

    namespace rel_ops_ {

        struct rel_ops_base { };

        template<typename T>
        inline bool operator > ( T const& x, T const& y ) {
            return y < x;
        }

        template<typename T>
        inline bool operator != ( T const& x, T const& y ) {
            return !(y == x);
        }

        // etc.
    }

    using rel_ops_::rel_ops_base;

// user

    struct X : rel_ops_base {
        // ...
    };

One problem I could imagine is that the operators would be used for more
than the X type, depending on the context. That would be a problem, too.
Oh well, guess the B&N is the way to go.

> Consider, the follow
>
> class X {
> public:
>    // ...
>    bool operator >(const X&) const;
>    bool operator ==(const X&) const;
>    bool operator <(const X&) const;
> };
> .....
>    if (x1 >= x2) foo();
>
> when processing the comparison, the compiler will see that there is no
> definition for >= so instead of generating an error message, let it try
> !x1.operator <(x2).

Defaults can be bad because they trade off safety for convenience. It's
like an implicit conversion operator for a class. Considering the
convenience of safer solutions that can be implemented in the language as
it is today, this proposed change doesn't have much value.

> This is more of a why not instead of a why.

Not the first time for this... :-)


[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Tero Pulkkinen <terop@modeemi.cs.tut.fi>
Date: 1999/08/23
Raw View

"Greg Brewer" <nospam.gregb@hal-pc.org> writes:
> Whenever a class is defined, there is always an implied copy constructor.  I
> guess one could say that there is also an implied void constructor if none
> are defined but I think that's a stretch.  In cases when I use a class to
> define my own datatype with a full complement of logical operators, I've
> wonder why it is always necessary to define all the logical operators.  If a
> class defines operators for less than, greater than and equal without
> defining greater than or equal, less than or equal, and not equal, whe
> couldn't the compiler imply the missing logical operators.

This can be done automatically pretty easily.

template<class T>
class Comparisions {
public:
   friend bool operator>(const T&,const T&) const;
   friend bool operator==(const T&,const T&) const;
   friend bool operator<(const T&,const T&) const;
   ...
};

See "Scientific&Engineering C++" -book by Barton&Nackman for more
information about this.

> Consider, the follow
> class X {
> public:
>    // ...
>    bool operator >(const X&) const;
>    bool operator ==(const X&) const;
>    bool operator <(const X&) const;
> };
> .....
>    if (x1 >= x2) foo();

then your class X will be just:
  class X : public Comparisions<X> {
  public:
  };

Note that the class X is given as template argument for its own baseclass.

> when processing the comparison, the compiler will see that there is no
> definition for >= so instead of generating an error message, let it try
> !x1.operator <(x2).

This should not be made automatic - Sometimes its useful to have
non-standard behaviors, which does not obey operator>= equals !operator<.

--
-- Tero Pulkkinen -- terop@modeemi.cs.tut.fi --


[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]