Topic: Allows to specify implementation when defining a class...


Author: kon@iki.fi (Kalle Olavi Niemitalo)
Date: Fri, 20 Dec 2002 19:29:46 +0000 (UTC)
Raw View
philippe_mori@hotmail.com ("Philippe Mori") writes:

>     class Derived : public Base {
>         void f1() = g;    // (4)
>     };
>     4) Function name alias

Is this truly an alias, or just a forwarding function?
Consider this:

  struct First {
    virtual void f();
  };

  struct Second: First {
    void g() = f; // makes g an alias of f
    // another possible syntax: using f = g;
  };

  struct Third: Second {
    void f(int);  // hides Second::f
    void g();     // overrides Second::g and thus First::f
  };

  int main() {
    Third t;
    static_cast<First&>(t).f();  // calls Third::g
  }

One might want a similar feature for data members as well.

---
[ 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.jamesd.demon.co.uk/csc/faq.html                       ]





Author: philippe_mori@hotmail.com ("Philippe Mori")
Date: Sun, 22 Dec 2002 07:13:23 +0000 (UTC)
Raw View
>
>     This has been brought up a few times. I think the most recent one was
by
> David Schwartz. The idea was to have a constructor use the same syntax we
> currently use for invoking base class constructors, coupled with
> overloading:
>
> class MyClass
> { // error checking and the like ommited
> private:
>     static ListEntry<MyClass> head, tail;
>
> private:
>     ListEntry<MyClass> le;
>     int i, j, k;
>     std::string name;
>
>     MyClass(const char *n = "<no name>")
>         : le(this), i(0), j(0), k(1234), name(n)
>     {
>         ::AddToTrackHash(this); // hypothetical function to track object
>
>         le.AttachBefore(&tail);
>     }
>
>     MyClass(int eye = 0, int jay = 0, int kay = 1234, const char *n = "<no
> name>")
>         : MyClass(n), i(eye), j(jay), name(n)
>     {
>         k = kay;
>     }
> };
>

I would think that when a constructor forward to another constructor at the
same level, we won't allows to uses initialisation list for other member as
enabling it would make it far too complex (what will be the initialisation
order, what will happen if an exception is thrown,...). This would be
particulary complex if both constructor are not in the same translation
unit...

Also in a simple like the one you give, one would probably put the complex
code in the constructor that have most arguments (maybe even a private
constructor just for that purpose).


---
[ 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.jamesd.demon.co.uk/csc/faq.html                       ]





Author: nikb@webmaster.com ("Nikolaos D. Bougalis")
Date: Sun, 22 Dec 2002 16:53:44 +0000 (UTC)
Raw View
""Philippe Mori"" <philippe_mori@hotmail.com> wrote in message
news:PY0N9.7953$cN3.1375779@news20.bellglobal.com...
> >
> >     This has been brought up a few times. I think the most recent one
was
> by
> > David Schwartz. The idea was to have a constructor use the same syntax
we
> > currently use for invoking base class constructors, coupled with
> > overloading:
> >
> > class MyClass
> > { // error checking and the like ommited
> > private:
> >     static ListEntry<MyClass> head, tail;
> >
> > private:
> >     ListEntry<MyClass> le;
> >     int i, j, k;
> >     std::string name;
> >
> >     MyClass(const char *n = "<no name>")
> >         : le(this), i(0), j(0), k(1234), name(n)
> >     {
> >         ::AddToTrackHash(this); // hypothetical function to track object
> >
> >         le.AttachBefore(&tail);
> >     }
> >
> >     MyClass(int eye = 0, int jay = 0, int kay = 1234, const char *n =
"<no
> > name>")
> >         : MyClass(n), i(eye), j(jay), name(n)
> >     {
> >         k = kay;
> >     }
> > };
> >
>
> I would think that when a constructor forward to another constructor at
the
> same level, we won't allows to uses initialisation list for other member
as
> enabling it would make it far too complex (what will be the initialisation
> order, what will happen if an exception is thrown,...). This would be
> particulary complex if both constructor are not in the same translation
> unit...

    Which is exactly why I said that some issues (like the "allow
initialization" problem, which I mentioned in my original post) need to be
addressed. I do feel that this proposal has merit, and would like to here
opinions, potential problems, etc, from others in this group.

> Also in a simple like the one you give, one would probably put the complex
> code in the constructor that have most arguments (maybe even a private
> constructor just for that purpose).

    I think that would probably be the most "sane" solution, although I can
imagine a couple of (rather obscure) cases, where one may want to do this
"constructor delegation" more than once... But that could get messy.

    -n

---
[ 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.jamesd.demon.co.uk/csc/faq.html                       ]





Author: philippe_mori@hotmail.com ("Philippe Mori")
Date: Thu, 19 Dec 2002 17:52:02 +0000 (UTC)
Raw View
I would like the possibility to specify the implementation of some functions
in the following way (or something similar):

    class Derived : public Base {
        Derived(...) = Base;    // (1)

        Derived(const string &r) =
            Derived(const char * = r.c_str());  // (2)

        Derived(const Derived &) = default;    // (3)

        void f1() = g;    // (4)

        char & f2();
        const char & f2() const = f2;    // (5)

        Derived &operator=(const Derived &) = void;    // (6)

        void reset() = for_each(reset);    // (7)
    };

    // (8) -- or = std ?
    bool operator= =(const Derived &, const Derived &) = default;

This would allows to:
    1) Uses constructors from a base class without having to redefine them
all.
    2) Forward to another constructor at the same level after possibly some
arguments modifications.
    3) Uses default compiler provided implementation.
    4) Function name alias
    5) Share implementation for const member function. In that case, the
compiler would deduce that we want the code of the non-const version (or
vice-versa). The compiler would be smart enough to do proper validation (or
if not possible issue an error). If the function is not inline, the error
may be generated only at link...
    6) Prevent calling that function.
    7) Call a function on all base and member
    8) Default implementation for some operators. For example, = = could
check the equality of all member (in declared order).
    ...

It would be nice to be able to specify default implementation for all
operators (in a group). So that if we define operator += for a class,
binary+ could be automatically defined...

A few "reserved" words would allows to choose the proper implementation. For
exemple, we could have a word that would tell a constructor to initialize
all member to default or to repeat something on all members (always or if a
member exists as specified by the programmer).

Note that some case like (7) and (8) would probably be better handled by
having a "list" of member and/or base on which an action should be
applyed... This could be similar to the "list" type proposed by some for
template parameters...

For case like (5), maybe it could be an additionnal keyword that would
allows to select return type from the constness of the object. For ex.
qualifier(const, this) char &f2() ; This should also works for standalone
function: qualifier(const volatile, a) char & f(char & a); The syntax here
is just an example...


What do you think about having a similar feature in the language?

---
[ 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.jamesd.demon.co.uk/csc/faq.html                       ]





Author: Daniel.Miller@tellabs.com ("Dan'l Miller")
Date: Thu, 19 Dec 2002 19:39:47 +0000 (UTC)
Raw View
Philippe Mori wrote:
> I would like the possibility to specify the implementation of some functions
> in the following way (or something similar):
>
>     class Derived : public Base {
>         Derived(...) = Base;    // (1)
>
>         Derived(const string &r) =
>             Derived(const char * = r.c_str());  // (2)
>
>         Derived(const Derived &) = default;    // (3)
>
>         void f1() = g;    // (4)
>
>         char & f2();
>         const char & f2() const = f2;    // (5)
>
>         Derived &operator=(const Derived &) = void;    // (6)
>
>         void reset() = for_each(reset);    // (7)
>     };
 >
>     // (8) -- or = std ?
>     bool operator= =(const Derived &, const Derived &) = default;
....snip...
> What do you think about having a similar feature in the language?

   It is nice that with the exception of #7 you posit adding no new keywords.
#7 goes in the wrong direction in this regard.  (I chuckled at the creative
re-use of the default keyword.  Heretofore I thought only static and extern were
eligible :-) for far-afield re-use, based on past precedent.)

   Regarding #4 and #5, how would you posit handling ambiguous left-hand-sides
of the =?  In #4 what if there are overloaded g member-functions (or a g
member-function and a ::g function)?  In #5 what if there is also a third
overloaded f2 member-function (or a ::f2 function)?

   Have you implemented this in a live C++ compiler?  Or have you convinced
someone else to implement this for you in a live C++ compiler?  That will allow
you to launder any dirty laundry yourself as part of
self-vetting/self-critiquing this idea (as well as exploring the troublesome
cases which I mention above).

   Also, I think C++ already permits specification of too much implementation
detail in the class definition.  This idea increases the amount of
implementation detail in the class definition, which is trending in the wrong
direction in my opinion, as changing your mind in these implementation details
in a header file will likely cause more object files to rebuild than would
otherwise be necessary in the scenarios where interface & inlining status has
not changed.

   Oh, speaking of which, is it your intent that this member-function= syntax
would imply inline just as defining a member-function within a class definition
(instead of merely declaring a member-function) does, since this
member-function= syntax also appears within the class definition?

   In summary, consider all interactions of your idea vis a vis all other
existing C++ language features and itemize how each intersection would be
handled.  (Someone needs to do that anyway when implementing in a compiler.
Someone should do that anyway when standardizing.  These duties might as well be
on your shoulders.)

---
[ 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.jamesd.demon.co.uk/csc/faq.html                       ]





Author: allan_w@my-dejanews.com (Allan W)
Date: Fri, 20 Dec 2002 00:09:15 +0000 (UTC)
Raw View
philippe_mori@hotmail.com ("Philippe Mori") wrote
> I would like the possibility to specify the implementation of some functions
> in the following way (or something similar):
>
>     class Derived : public Base {
>         Derived(...) = Base;    // (1)
> Uses constructors from a base class without having to redefine them
> all.

Interesting idea. Does this come up often? If so, don't your derived
classes generally have one or two new data members? Shouldn't the derived
constructors do SOMETHING, then?

>         Derived(const string &r) =
>             Derived(const char * = r.c_str());  // (2)

> Forward to another constructor at the same level after possibly some
> arguments modifications.

The usual advice is to factor out the common code:

          Derived(const char*c) { init(c); }
          Derived(std::string &r) { init(r.c_str()); }
          private: void init(const char*c) { ... }

I agree that this is not as good as declaring one constructor in
terms of another -- not only is it less convenient, but it doesn't
allow initialization syntax.

>         Derived(const Derived &) = default;    // (3)
> Uses default compiler provided implementation.

Your wish is granted -- if you don't declare a copy constructor, then
the compiler will write one for you. Usually works fine if your class
doesn't have any "owned" pointers.

>         void f1() = g;    // (4)
> Function name alias

You can achieve the same effect on an (even slightly-optimizing)
compiler by making f1() an inline function that calls g:
          inline void f1() { g(); }

>         char & f2();
>         const char & f2() const = f2;    // (5)
> Share implementation for const member function. In that case, the
> compiler would deduce that we want the code of the non-const version
> (or vice-versa). The compiler would be smart enough to do proper
> validation (or if not possible issue an error). If the function is
> not inline, the error may be generated only at link...

Wish granted -- well, even better -- one function can handle both const
and non-const objects. Just write the const version, and don't declare
one that isn't const. When your object is non-const, the function will
still be callable.

>         Derived &operator=(const Derived &) = void;    // (6)
> Prevent calling that function.

You can already do this:
    class myclass {
       // ...
    private:
        void operator=(const myclass&); // Declared but not defined
    };
Because it's private, code outside the class cannot even call it.
Because it's not defined, code inside the class gets a link error
if it's called.

I do like your syntax better, though. It makes it obvious that this
was a deliberate design decision, not an omission.

>         void reset() = for_each(reset);    // (7)
> Call a function on all base and member

Call the same function on ALL bases and member variables? How often
would you need this? Generally they're not even the same types. Can
you provide an example where it's useful?

>     };
>
>     // (8) -- or = std ?
>     bool operator= =(const Derived &, const Derived &) = default;
> Default implementation for some operators. For example, = = could
> check the equality of all member (in declared order).

Memberwise equality. An interesting idea. You'd do this type of thing
"for some operators" -- which ones? All of the comparison operators?

The trouble is that most classes aren't currently designed for this.
The declared order often has nothing to do with logical sorting, and
the definitive use for mutable members is to form a "cache" which
could have nothing to do with the object's actual state.

Would you always compare base objects before the derived object members?
Vice-versa might make more sense. How would you handle objects of
different (most-derived) types -- such as Derived versus Derived2?
How about two objects that are BOTH derived from the current class type?

> It would be nice to be able to specify default implementation for all
> operators (in a group). So that if we define operator += for a class,
> binary+ could be automatically defined...

I like this. That's suggested practice anyway, but it makes for tedious
typing.

> A few "reserved" words would allows to choose the proper implementation. For
> exemple, we could have a word that would tell a constructor to initialize
> all member to default or to repeat something on all members (always or if a
> member exists as specified by the programmer).

If you don't list a member in a constructor's initializer-list, it gets
default constructed.

> Note that some case like (7) and (8) would probably be better handled by
> having a "list" of member and/or base on which an action should be
> applyed... This could be similar to the "list" type proposed by some for
> template parameters...

That would be more useful and less dangerous, yes. But if it isn't
automatic, why not just write the function yourself?

> For case like (5), maybe it could be an additionnal keyword that would
> allows to select return type from the constness of the object. For ex.
> qualifier(const, this) char &f2() ; This should also works for standalone
> function: qualifier(const volatile, a) char & f(char & a); The syntax here
> is just an example...

Not needed. If a member function is declared const, it also works on
non-const objects. Similarly for volatile.

> What do you think about having a similar feature in the language?

Like some, not all.

---
[ 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.jamesd.demon.co.uk/csc/faq.html                       ]





Author: nikb@webmaster.com ("Nikolaos D. Bougalis")
Date: Fri, 20 Dec 2002 16:55:50 +0000 (UTC)
Raw View
"Allan W" <allan_w@my-dejanews.com> wrote in message
news:7f2735a5.0212191602.140c6fea@posting.google.com...

> >         Derived(const string &r) =
> >             Derived(const char * = r.c_str());  // (2)
>
> > Forward to another constructor at the same level after possibly some
> > arguments modifications.
>
> The usual advice is to factor out the common code:
>
>           Derived(const char*c) { init(c); }
>           Derived(std::string &r) { init(r.c_str()); }
>           private: void init(const char*c) { ... }
>
> I agree that this is not as good as declaring one constructor in
> terms of another -- not only is it less convenient, but it doesn't
> allow initialization syntax.

    As you said, the "solution" that currently has to be employed, is to
write a member function, and call it from within the body of the
constructor. This of course, is a viable, albeit not perfect solution. It
does not address the issue with objects initialized in the ctor initializer
list, and it is not clear what the "init" (or whatever you wish to call it)
function does; and it cannot possibly work for the construction of const
object.

    This has been brought up a few times. I think the most recent one was by
David Schwartz. The idea was to have a constructor use the same syntax we
currently use for invoking base class constructors, coupled with
overloading:

class MyClass
{ // error checking and the like ommited
private:
    static ListEntry<MyClass> head, tail;

private:
    ListEntry<MyClass> le;
    int i, j, k;
    std::string name;

    MyClass(const char *n = "<no name>")
        : le(this), i(0), j(0), k(1234), name(n)
    {
        ::AddToTrackHash(this); // hypothetical function to track object

        le.AttachBefore(&tail);
    }

    MyClass(int eye = 0, int jay = 0, int kay = 1234, const char *n = "<no
name>")
        : MyClass(n), i(eye), j(jay), name(n)
    {
        k = kay;
    }
};

    The syntax for calling the constructor conforms to existing usage for
calling base class constructors, and utilizes the familiar rules of function
overloading. The constructor in the initializer list is ran first, and upon
its return, constuction continues as it normally would have, down the
initializer list, and into the body.

    Some issues need to be addressed of course. Preventing a constructor
from calling itself, or calling the constructor that called it, is one such
issue and the semantics of the constructor initializer list in a constructor
that invokes another (e.g. should it be able to call base-class
constructors?) is another, to name a couple.

> >         Derived &operator=(const Derived &) = void;    // (6)
> > Prevent calling that function.
>
> You can already do this:
>     class myclass {
>        // ...
>     private:
>         void operator=(const myclass&); // Declared but not defined
>     };
> Because it's private, code outside the class cannot even call it.
> Because it's not defined, code inside the class gets a link error
> if it's called.

    Still, some way to indicate that a function is not available at all
would be nice. A proposal to have "explicit class <name>" was discussed here
a while back, but was, if I recall correctly, shot down.

    -n

---
[ 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.jamesd.demon.co.uk/csc/faq.html                       ]