Topic: [Proposal] Inheriting from reference
Author: loufoque@remove.gmail.com (loufoque)
Date: Sun, 19 Nov 2006 18:13:34 GMT Raw View
Andrei Polushin wrote:
>
> Syntax
> ======
>
> class Widget : public Window& { // *derive from reference*
> public:
> Widget()
> : Window(createWindow()) // initialize reference
> {}
> // ...
> };
>
> This solves the problem and does not create more problems.
>
> - We must initialize reference, so it is always initialized;
> - We cannot rebind reference, but probably we don't need that;
> - There is no ambiguity in derived class members' access, as
> it was with overloaded operator.()
>
> The behavior for inheritance from reference is otherwise identical
> to usual inheritance, and the behavior of resulting "smart reference"
> is identical to usual reference, so I expect the idea will fit well
> into language design.
>
> Could it be accepted? I'm waiting for your comments.
That seems interesting because since Widget inherits from a reference to
Window, then functions taking references to Window can take references
to Widget instead.
This allows to forward operator overloading for the underlying type etc.
---
[ 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: musiphil@bawi.org (Seungbeom Kim)
Date: Mon, 20 Nov 2006 00:41:37 GMT Raw View
loufoque wrote:
> Andrei Polushin wrote:
>>
>> Syntax
>> ======
>>
>> class Widget : public Window& { // *derive from reference*
>> public:
>> Widget()
>> : Window(createWindow()) // initialize reference
>> {}
>> // ...
>> };
>
> That seems interesting because since Widget inherits from a reference to
> Window, then functions taking references to Window can take references
> to Widget instead.
Even if Widget is derived from Window (and not a reference to Window),
functions taking Window& can take Widget&.
struct Window { };
struct Widget : Window { };
void foo(Window&);
void bar(Widget& w) { foo(w); }
This is perfectly valid.
--
Seungbeom Kim
---
[ 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: loufoque@remove.gmail.com (Mathias Gaunard)
Date: Mon, 20 Nov 2006 16:32:45 GMT Raw View
Seungbeom Kim wrote:
> Even if Widget is derived from Window (and not a reference to Window)
In that case Widget is not a smart reference. Which was the point, here.
Smart references need to be convertible to normal references, which
seems to be quite clearly allowed by this inheritance-like proposal.
---
[ 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: Thu, 9 Nov 2006 00:09:35 GMT Raw View
Andrei Polushin ha scritto:
>
> So I propose an explicit
>
> class Widget : public Window& { // *derive from reference*
> public:
> Widget()
> : Window(createWindow()) // initialize reference
> {}
> // ...
> };
>
I admit that I'm not too familiar with the problem. I don't have a copy
of D&E and googling for "smart reference" gives a lot less meaningful
hits than I expected. However, the inheritance idea looks intriguing.
Instead of adding a new syntax, I would prefer using a class template
that, through some unspecified compiler magic, does the job. For example:
class Widget : public std::delegate<Window> {
public:
Widget()
: std::delegate<Window>(createWindow()) // initialize reference
{}
// ...
};
Where std::delegate<T> is this "magic" class type that is initialized
with a T& (without making a copy) and acts as a perfect delegate of the
referenced T object. In practice, the only difference from a normal T&
is that std::delegate<T> is a class type that you can inherit from. I'm
not a compiler writer, but it might not be too difficult to implement.
In fact, if it weren't for data members it's quite simple:
1) provide an (explicit?) constructor that takes a T&
2) provide a trivial dtor
3) class will be non-copyable, so declare but not define private copy
ctor and assignment operator
4) provide a implicit conversion operator to T&
5) for each public non-static member function (whether virtual or
non-virtual) in T provide an inline wrapper function that perform
delegation. Notice that virtual functions in T will become non-virtual
in std::delegate<T>: this is intentional.
6) for each public non-static member function template provide a
suitable wrapper similar to point 5.
Except for point 3, all members of std::delegate<T> are declared public.
Notice that non-public members are not imported. Alternatively, we could
allow protected members to be imported as protected, but it might be
seen as a violation of the access specifier (actually, std::delegate<T>
and T are unrelated classes, to std::delegate<T> should not have access
to non-public members).
About data membera, we have two choices:
1) disallow completely: i.e. if T has public non-static data members,
std::delegate<T> is ill-formed;
2) allow them: in that case if d is an instance of std::delegate<T> and
x a non-static public data member of T then the compiler shall interpret
d.x as d.t.x where t is the reference to a T object stored in d.
Number 1 seems too draconic, but number 2 seems more difficult to implement.
Just my 2 eurocent,
Ganesh
PS: of course, the name std::reference<T> could also be used.
---
[ 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: "Andrei Polushin" <polushin@gmail.com>
Date: Sat, 4 Nov 2006 19:27:45 CST Raw View
Hi,
Many years ago, there was a long-term discussion about overloading
operator.() for smart references. Neither idea was accepted, however.
Now let me propose a compromise solution for that old problem. Note
that I do not propose overloading operator.(), but suggest a more
idiomatic way to deal with the problem.
For the background information and problem statement, refer to
[1] B.Stroustrup, Design and Evolution of C++,
11.5.2 Smart references
[2] B.Stroustrup, Design and Evolution of C++,
12.7 Delegation
[3] Discussions in comp.std.c++
(search for "smart reference")
Use case
========
Imagine we have a GUI system that creates objects of type Window:
class Window {
Window();
public:
void f();
// ...
};
Window& createWindow(); // factory function
Window w& = createWindow(); // ok
We need to inherit from Window to create Widget objects, but Window
is not inheritable: it has private constructor:
class Widget : public Window { // error: cannot inherit
// ...
};
We actually need to inherit, because we need our Widget to have all
that thousands of Window member functions.
We can try to define the Widget as smart pointer, but what if we need
a smart pointer to the Widget itself?
class Widget : auto_ptr<Window> {
public:
Widget() : auto_ptr(&createWindow()) {}
void g();
}
auto_ptr<Widget> p(new Widget());
p->g(); // ok
p->get()->f(); // very, very, very ugly
Probably, we need a smart reference with overloaded operator.():
class Widget {
Window& w;
public:
Widget() : w(createWindow()) {}
Window& operator.() { return w; }
// ...
};
But we are not allowed to overload operator.() for many severe reasons.
Now recall a virtual inheritance that may help:
class Widget : virtual public Window {
public:
Widget()
: Window(createWindow()) // copied by value
{}
// ...
};
Now it's copied by value, but we know that virtual base class is
*referred* from derived class using some /interesting mechanism/
(which I will not describe here). In fact, we have a not-so-smart
reference from derived (Widget) to base (Window). So why do we need
to copy Window by value? I want to initialize that reference instead.
So I propose an explicit
Syntax
======
class Widget : public Window& { // *derive from reference*
public:
Widget()
: Window(createWindow()) // initialize reference
{}
// ...
};
This solves the problem and does not create more problems.
- We must initialize reference, so it is always initialized;
- We cannot rebind reference, but probably we don't need that;
- There is no ambiguity in derived class members' access, as
it was with overloaded operator.()
The behavior for inheritance from reference is otherwise identical
to usual inheritance, and the behavior of resulting "smart reference"
is identical to usual reference, so I expect the idea will fit well
into language design.
Could it be accepted? I'm waiting for your comments.
--
Andrei Polushin
---
[ 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: "Andrei Polushin" <polushin@gmail.com>
Date: Sun, 5 Nov 2006 23:02:40 CST Raw View
Andrei Polushin wrote:
> So I propose an explicit
>
> Syntax
> ======
>
> class Widget : public Window& { // *derive from reference*
> public:
> Widget()
> : Window(createWindow()) // initialize reference
> {}
> // ...
> };
>
> This solves the problem and does not create more problems.
>
> - We must initialize reference, so it is always initialized;
> - We cannot rebind reference, but probably we don't need that;
> - There is no ambiguity in derived class members' access, as
> it was with overloaded operator.()
>
> The behavior for inheritance from reference is otherwise identical
> to usual inheritance, and the behavior of resulting "smart reference"
> is identical to usual reference, so I expect the idea will fit well
> into language design.
Re-reading D&E 12.7, I've noticed a relevant question: what if the
base class contains virtual functions - should they be overridden in
class derived by reference? I see several alternate solutions:
1. Derived does never override functions from Base,
no diagnostics required (harmful and inconsistent);
2. Derived is unable to override functions from Base,
any attempt to override will fail to compile (inconvenient,
but better than nothing; compatible with other alternatives);
3. Derived can override members of Base that expects it.
Base class is marked as target for "derivation by reference"
and each instance of derived class contains a copy of VTable
suitable for dispatching its virtual calls (memory consuming,
complicated);
4. Derived can override members of Base.
An effective *dynamic type* of Derived is created at runtime
from the combination of actual Base1 type and Derived type.
This involves the runtime copying of Base1::VTable with
subsequent overriding its relevant entries by those from
Derived::VTable. The dynamic type is created once for each pair
of <Base1,Derived> and cached by the runtime. (Most viable and
preferable, but requires additional memory allocation).
The last variant is like a prototype-based inheritance available in
some other languages (JavaScript, Python 2.2, etc.). Despite its
benefits, it will require additional memory for each dynamic type.
Some alternatives for the memory allocation:
1. use ::operator new();
2. use std::allocator();
3. use some specific allocator (implementation-defined);
4. any of the above, and provide a limited static memory area
for each case where the dynamic type could be created.
Probably, the allocation issue is completely implementation-defined.
I prefer the use of specific allocator and the limited static memory
area preallocated in advance, if possible to predict its size.
--
Andrei Polushin
---
[ 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 ]