Topic: Defect Report: defined pure virtual functions needlessly undefined during [de]construction


Author: "Bill Wade" <bill.wade@stoner.com>
Date: 2000/06/29
Raw View
"Jonathan de Boyne Pollard" <J.deBoynePollard@tesco.net> wrote

> On the gripping hand, that argument that C++ doesn't require "smart"
linkers
> doesn't hold water *anyway*, whichever view one takes on the issue of weak
> externals.  Many of the other features of C++, such as templates and
inline
> functions with external linkage, require linker mechanisms (such as
segments
> with "common" combine types) that are also regarded as the sole province
of
> "smart" linkers.

Two points:

Requiring a linker to support weak references is different from requiring it
to support COMMON, they are different kinds of smart.  You need COMMON for
fortran 66, and I'm not aware that you need weak references for any flavor
of fortran.

Also, it is possible (with considerable runtime time and space penalties) to
implement COMMON as used in C++ without linker support.

I'm not entirely sure what the implementation mechanisms for 'export' are
likely to be.



---
[ 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: Jonathan de Boyne Pollard <J.deBoynePollard@tesco.net>
Date: 2000/06/13
Raw View
BW> Consider a typical vtable/compiler/linker implementation.
BW>
BW> Normal virtual function: The compiler, in most translation units
BW> tells the linker "put the address of that particular virtual
BW> function into the vtable right here.  Scream bloody murder if you
BW> never see the function."  In one translation unit the compiler
BW> tells the linker "here is the function."
BW>
BW> Pure-virtual function:  The compiler sticks a zero in the vtable.
BW>
BW> The rule in question means that compilers don't need to make
BW> vtables that contain an entry that tell the linker "put the
BW> address of ABC::foo() right here if it gets linked in, otherwise
BW> put a zero".
BW>
BW> That would take a particular kind of "smart linker" that is
BW> not otherwise needed for the C++ standard.  I don't know how
BW> common that feature is in linkers.

I find it somewhat ironic that this feature is regarded as "smart", because it
puts the people making the claim that only "smart" linkers have such features
in the rather invidious position of having to admit that Microsoft writes
better linkers (for DOS, no less!) than the "dumb" ones that are available for
other platforms.  (-:  Microsoft's LINK has supported weak externals since at
least the days of MASM version 6.0 .

Of course there is another way of looking at this that eliminates the
embarrassment (to some) of having to praise Microsoft:  The fact that
Microsoft's LINK supports weak externals, combined with the statement made
elsewhere in this thread that IBM's mainframe linkers have "always supported
weak externals", tends to indicate that weak externals are less of a "smart"
feature and more of a "run of the mill" feature.

But this alternative perspective removes the foundations upon which rests the
oft-given argument that "C++ should not force an implementation to have a
`smart' linker.".  If this feature is a humdrum one that any reasonable linker
should have, then C++ wouldn't be requiring a "smart" linker if C++
implementations had to make use of weak externals.  It would merely be
requiring a mediocre linker.

On the gripping hand, that argument that C++ doesn't require "smart" linkers
doesn't hold water *anyway*, whichever view one takes on the issue of weak
externals.  Many of the other features of C++, such as templates and inline
functions with external linkage, require linker mechanisms (such as segments
with "common" combine types) that are also regarded as the sole province of
"smart" linkers.

---
[ 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: "cuibo" <cuibo@email.msn.com>
Date: 2000/06/05
Raw View
I am sorry , but my opinion is to define a class.

                                                Cui Bo
Jim Hill wrote in message <1eaxusx.1nj831n1s8jaeN%jthill@telus.net>...
>Stephen Clamage <stephen.clamage@sun.com> wrote:
>> So what should be the requirement in the case where T::f is not
>> defined?
>
>That's not the issue, but the answer to it is: undefined behavior, just
>as it is now for all calls to undefined functions.
>
>The issue is: what should be the requirement when T::f _is_ defined?
>
>As the standard reads now, a virtual call to a defined function produces
>undefined behavior solely because the function is declared pure virtual.
>
>To resurrect a condensed version of my example from the original post:
>
>struct B     { B(int); virtual void reset(int)    ; private: int v0; };
>struct D : B { D(int); virtual void reset(int)    ; private: int v; };
>
>void D::reset(int a)     { v = a; }
>     D::D(int a) : B(a)  { reset(a); }
>
>void B::reset(int a)     { v0 = a; }
>     B::B(int a)         { reset(a); }
>
>Change `reset(int)` to `reset(int) = 0` in the first line, and the last
>line suddenly produces undefined behavior.
>
>Why? Who benefits?
>
>It gets worse:
>
>struct B2 { B2(); virtual void foo(), bar(), baz() = 0; };
>     B2::B2()  { foo(); }
>void B2::foo() { bar(); }
>void B2::bar() { }
>
>Now, you realize bar() requires an override.  As the Standard is now,
>the best way I can find to do it is inline-by-editor foo() in the
>constructor, change any bar() calls to B2::bar() calls, and add a note
>to foo()'s source that all changes must be repeated in the constructor.
>
>I repeat: what's 10.4p6 (and the rest of the language cited in the
>report) supposed to achieve?
>
>Jim
>
>P.S.:
>> You can't make it a constraint violation, because it can't always be
>> detected, even in principle
>
>Yep <blush> - I dropped a "not" in my original report.
>
>---
>[ 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              ]
>



---
[ 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: jthill@telus.net (Jim Hill)
Date: 2000/06/01
Raw View
Just a few final clarifications, and I'm done with this. There are
workarounds for all but one situation I haven't hit yet (see below) and
the decision wasn't, and isn't, mine to make.

<wmm@fastdial.net> wrote:

> But you _can_ call a defined pure virtual, as long as you
> explicitly qualify its name.

Explicitly qualified calls are not virtual. Only an unqualified name
refers to the virtual function, whose definition is appropriate to the
dynamic type of the object.

> The only case I can think of where this issue might matter beyond a
> trivial syntactic convenience is [...

struct B2 { B2(); virtual void foo(), bar(), baz() = 0; };
     B2::B2()  { foo(); }
void B2::foo() { bar(); }
void B2::bar() { }

Later, you realize bar() requires an override.

> ...]

"Trivial" is a judgement call. I don't consider well-defined and
necessary compile-time enforcement of an interface requirement (else why
were pure virtuals added?), but undefined behavior at runtime, trivial.

> That seems like a pretty rare circumstance to justify a major new
> requirement for implementors.

At least some of that rarity is because of the lack of support, no? Now
I know that C++ can't specify that a virtual must be overridden if I
intend to use it.

Asd I have to question "major".  It's one bit in a symbol table, checked
to decide whether to do an explicit search and/or what to do if it
fails.

> I don't think most people find this requirement especially onerous. [...]
> I'm not saying that it never occurs, but it surely can't be very
> widespread or people would have been clamoring for this change years ago.

As I said: point taken, and it's not my call, but I find such arguments
suspect.

Jim

---
[ 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: jthill@telus.net (Jim Hill)
Date: 2000/05/31
Raw View
Bill Wade <bill.wade@stoner.com> wrote:

> an entry that tells the linker "put the address of ABC::foo() right
> here if it gets linked in, otherwise put a zero".

That's a "weak reference", apparently widely regarded as a bit exotic,
much as long external names were once regarded.=20

Also similar is the effect of their absence: it produces undefined
behavior in situations surprising to those unused to doing without, who
may regard this as a defect.

A further and rather ironic similarity is that C++ arguably drove IBM to
finally put long-name support in their mainframe linkers, which afaik
have always suppported weak references.

J=F6rg Barfurth wrote in <20000524.17323789@jb-11116.stardiv.de>:
> that is what 'pure virtual' means in C++: cannot be called=20
> virtually.

Mike Miller wrote in email: ~the whole point of pure virtuals is that
you don't have to define them~.

I argue that the meaning of pure virtuals is precisely that they must be
overridden. Without them, that requirement can only be checked at
runtime; with them, the language can enforce it.=20

In most cases, the declaration is only a declaration: the function
itself will (or should) never be called. That, I think, is why they're
called "pure" virtuals. I can find no other reason for declaring a
function one does not wish to define.

But most cases is not all cases: one may want to define, as well as
declare, functions that must be overridden.

C++ knows this: one _can_ provide a definition for such functions. This
makes "pure" virtuals misnamed; "abstract" virtuals would be more
accurate, as their presence in the interface is the defining
characteristic of an abstract base class.=20

But the absence of weak-reference support renders what support remains
worse than useless: the machinery is required, but it's not hooked up to
anything. The attempt is permitted and produces undefined behavior in
all cases.

This problem arose, I think, because C++ tries to straddle the fence
between pure virtual declarations and abstract[ed] virtual functions. If
the intent had been only to support pure virtuals, definition of those
functions would, or should, have been prohibited: defining the function
makes its declaration not a pure declaration. As the standard reads now,
it's not virtual, either.

=3D=3D=3D

Mr. Miller also pointed out that there hasn't been much perceived demand
for full abstract-virtual support, and one can always just not declare
them pure. I take his point, but I have to say I find such arguments
suspect for a number of reasons. Long-name support was dismissed, if
that isn't too strong a word, with just such arguments. We have, what,
billions of lines of code written with short names? And yet that
obviously wasn't sufficient to show it was useless. We'd all have been
better off if IBM and any others had given in long ago.=20

=3D=3D=3D

Let me finish by describing the intent of the design that drove this
report: it's an "inflight (or pending) change" object, initialized from
the object that is to be changed. The original value, or a reference to
it (I hadn't made that choice when I hit this limitation) needs to be
preserved (by locking or by copying) in the pending-change object, for
validation, for audit and for use by "revert" actions by the user.

The design would also be useful in the "Do-Undo" classes being discussed
in clc++m; especially for cases where a blind reset is expensive and
perhaps unnecessary.

In all these cases, the constructor must record the original and current
values -- but precisely which parts of the full current value depends on
the nature of the change to be made; and the destructor for all such
changes must apply any changes and release any serialization it has
acquired. If the serialization and update are done anyplace else, you no
longer have a "pending change" object.=20

Expressing this intent in the (only) way C++ provides produces undefined
behavior. That, I call a Defect.

Jim

---
[ 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: wmm@fastdial.net
Date: 2000/06/01
Raw View
In article <1eb6lhm.x9fgpiglf2hkN%jthill@telus.net>,
  jthill@telus.net (Jim Hill) wrote:
> Bill Wade <bill.wade@stoner.com> wrote:
>
> > an entry that tells the linker "put the address of ABC::foo() right
> > here if it gets linked in, otherwise put a zero".
>
> That's a "weak reference", apparently widely regarded as a bit exotic,
> much as long external names were once regarded.=20

Let me elaborate a bit here, based on the email exchange Jim
mentioned.  A "weak reference" is a reference that the linker
fills in only if a definition is available; if it is not, the
link still succeeds, but a null pointer is substituted where
the pointer to the definition would have been.  This is in
contrast to the traditional linker, where all references must
be satisfied or else the link fails.

Jim's proposal is essentially that the C++ Standard mandate
the use of a linker capable of supporting weak references, and
that references in the vtable to pure virtual functions be
emitted as weak.  (Or equivalently, that some technique other
than traditional vtables be used to implement virtual function
dispatch.)

> J=F6rg Barfurth wrote in <20000524.17323789@jb-11116.stardiv.de>:
> > that is what 'pure virtual' means in C++: cannot be called=20
> > virtually.
>
> Mike Miller wrote in email: ~the whole point of pure virtuals is that
> you don't have to define them~.
>
> I argue that the meaning of pure virtuals is precisely that they must
be
> overridden. Without them, that requirement can only be checked at
> runtime; with them, the language can enforce it.=20
>
> In most cases, the declaration is only a declaration: the function
> itself will (or should) never be called. That, I think, is why they're
> called "pure" virtuals. I can find no other reason for declaring a
> function one does not wish to define.
>
> But most cases is not all cases: one may want to define, as well as
> declare, functions that must be overridden.
>
> C++ knows this: one _can_ provide a definition for such functions.
This
> makes "pure" virtuals misnamed; "abstract" virtuals would be more
> accurate, as their presence in the interface is the defining
> characteristic of an abstract base class.=20
>
> But the absence of weak-reference support renders what support remains
> worse than useless: the machinery is required, but it's not hooked up
to
> anything. The attempt is permitted and produces undefined behavior in
> all cases.

But you _can_ call a defined pure virtual, as long as you
explicitly qualify its name.  I don't think most people find
this requirement especially onerous.  The only case I can
think of where this issue might matter beyond a trivial
syntactic convenience is where an abstract-base constructor
calls a function that takes a pointer/reference to that class,
passing "this" as the argument, that function calls a pure
virtual function, and the function might also be called from
elsewhere for a complete object.  That seems like a pretty
rare circumstance to justify a major new requirement for
implementors.  I'm not saying that it never occurs, but it
surely can't be very widespread or people would have been
clamoring for this change years ago.
--
William M. Miller, wmm@fastdial.net
OnDisplay, Inc. (www.ondisplay.com)


Sent via Deja.com http://www.deja.com/
Before you buy.

---
[ 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: "Bill Wade" <bill.wade@stoner.com>
Date: 2000/05/24
Raw View
"Jim Hill" <jthill@telus.net> wrote in message
news:1eaxusx.1nj831n1s8jaeN%jthill@telus.net...

> [ virtual calls to pure-virtual functions in constructors are illegal,
even if the function is actually defined ]
>
> Why? Who benefits?

Implementers.  Consider a typical vtable/compiler/linker implementation.

Normal virtual function: The compiler, in most translation units tells the
linker "put the address of that particular virtual function into the vtable
right here.  Scream bloody murder if you never see the function."  In one
translation unit the compiler tells the linker "here is the function."

Pure-virtual function:  The compiler sticks a zero in the vtable.

The rule in question means that compilers don't need to make vtables that
contain an entry that tell the linker "put the address of ABC::foo() right
here if it gets linked in, otherwise put a zero".

That would take a particular kind of "smart linker" that is not otherwise
needed for the C++ standard.  I don't know how common that feature is in
linkers.  It would be needed to allow different elements of a Fortran COMMON
block to be DATA'd in different translation units.  That would make
syntactic sense in Fortran, but I'm fairly sure that it is not actually
legal Fortran.  Any Fortran gurus care to comment?

As the standard currently reads, the vtable for an ABC can be full of zeros.
A non-virtual call to a pure virtual function lets the compiler tell the
linker that the function must exist.  None of this "give it to me if you've
got it, otherwise I don't care" stuff.

HTH



---
[ 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: =?ISO-8859-1?Q?J=F6rg?= Barfurth <joerg.barfurth@attglobal.net>
Date: 2000/05/25
Raw View

>>>>>>>>>>>>>>>>>> Urspr=FCngliche Nachricht <<<<<<<<<<<<<<<<<<

Am 24.05.00, 04:36:21, schrieb jthill@telus.net (Jim Hill) zum Thema Re:=20
Defect Report: defined pure virtual functions needlessly undefined during=
=20
[de]construction:


> Stephen Clamage <stephen.clamage@sun.com> wrote:
> > So what should be the requirement in the case where T::f is not
> > defined?

> That's not the issue, but the answer to it is: undefined behavior, just
> as it is now for all calls to undefined functions.

> The issue is: what should be the requirement when T::f _is_ defined?

> As the standard reads now, a virtual call to a defined function produce=
s
> undefined behavior solely because the function is declared pure virtual.

Yes. Actually that is what 'pure virtual' means in C++: cannot be called=20
virtually.
If an implementation uses vtables containing function pointers, it can=20
put a null function pointer into the vtable.
This also means that a defined pure virtual function is not considered=20
'used' unless called non-virtually. The linker can just drop it in that c=
ase.

> To resurrect a condensed version of my example from the original post:

> struct B     { B(int); virtual void reset(int)    ; private: int v0; };
> struct D : B { D(int); virtual void reset(int)    ; private: int v; };

> void D::reset(int a)     { v =3D a; }
>      D::D(int a) : B(a)  { reset(a); }

> void B::reset(int a)     { v0 =3D a; }
>      B::B(int a)         { reset(a); }

> Change `reset(int)` to `reset(int) =3D 0` in the first line, and the la=
st
> line suddenly produces undefined behavior.

What is so sudden about that ? You added a statement "Do not allow=20
virtual calls to this function", when actually you invoke such calls.

The 'undefined' means that (a) this is as undefined  as calling through a=
=20
null function pointer and (b) an optimizer that detects that it is a call=
=20
from a constructor can still call the defined version without checking=20
for a pure specifier.

> Why? Who benefits?

The implementer. It can implement pure virtual functions without linker=20
wizardry or additional special casing.

> > You can't make it a constraint violation, because it can't always be
> > detected, even in principle

Another reason to leave it undefined.

-- J=F6rg


---
[ 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: jthill@telus.net (Jim Hill)
Date: 2000/05/19
Raw View
Steve Clamage <stephen.clamage@sun.com> wrote:

>> For "base-class" in the above quote read "base-class-subobject", and
>> reread 12.7p3.

> Sorry, I don't see what you are getting at

The subject of 12.7p3 is virtual calls during [de]construction, but none
of your four example cases have either virtual calls or cdtors.  It's
certainly worthwhile to write your own examples to see the problem, but
you have to get those key features in there: virtual functions, called
during [de]construction.  There are more details in the original example
I posted in Message-ID: <1ea2ogf.11bq5q3kbnplrN%jthill@telus.net>.


Jim

---
[ 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: Stephen Clamage <stephen.clamage@sun.com>
Date: 2000/05/19
Raw View
On Wed, 17 May 2000 01:25:21 CST,
look@sig.please.because.this.is.invalid.bhp.com.au (Chris Kuan) wrote:

>stephen.clamage@sun.com (Steve Clamage) wrote in comp.std.c++:
>
>> ... we can call a base-class version of a function
>>overridden in a derived class, pure virtual or not, by qualifying the
>>function name with the base class name.
>>
>>What was your concern?
>
>AFAICS, Jim is objecting to the "undefinedness" in 10.4p6 on calling a pure
>virtual virtually within a cdtor. He would prefer that it behave (as in 12.7p3)
>as a "normal" virtual call (either that or just make it a constraint violation.)

You can't make it a constraint violation, because it can't always be
detected, even in principle:

class T {
public:
 virtual int f() = 0;
 T();
};
int g(T* tp) { return tp->f(); }
T::T() { g(this); }

No single part of this program fragment is invalid. It's only the
combination of passing "this" from constructor of an abstract class to
a function that calls a virtual function that is pure in the ctor's
class that is invalid. Since each of T, g, and T::T could be defined
in different translation units, it would take whole-program analysis
to detect the error. If the call to g in T::T were conditional, only
at run time could you detect the problem.

So what should be the requirement in the case where T::f is not
defined? Perhaps we could require an exception, but I don't recall
anyone ever suggesting that. The rule is that you can call the pure
virtual function explicitly, but otherwise the results are undefined.
---
Steve Clamage, stephen.clamage@sun.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: jthill@telus.net (Jim Hill)
Date: 2000/05/24
Raw View
Stephen Clamage <stephen.clamage@sun.com> wrote:
> So what should be the requirement in the case where T::f is not
> defined?

That's not the issue, but the answer to it is: undefined behavior, just
as it is now for all calls to undefined functions.

The issue is: what should be the requirement when T::f _is_ defined?

As the standard reads now, a virtual call to a defined function produces
undefined behavior solely because the function is declared pure virtual.

To resurrect a condensed version of my example from the original post:

struct B     { B(int); virtual void reset(int)    ; private: int v0; };
struct D : B { D(int); virtual void reset(int)    ; private: int v; };

void D::reset(int a)     { v = a; }
     D::D(int a) : B(a)  { reset(a); }

void B::reset(int a)     { v0 = a; }
     B::B(int a)         { reset(a); }

Change `reset(int)` to `reset(int) = 0` in the first line, and the last
line suddenly produces undefined behavior.

Why? Who benefits?

It gets worse:

struct B2 { B2(); virtual void foo(), bar(), baz() = 0; };
     B2::B2()  { foo(); }
void B2::foo() { bar(); }
void B2::bar() { }

Now, you realize bar() requires an override.  As the Standard is now,
the best way I can find to do it is inline-by-editor foo() in the
constructor, change any bar() calls to B2::bar() calls, and add a note
to foo()'s source that all changes must be repeated in the constructor.

I repeat: what's 10.4p6 (and the rest of the language cited in the
report) supposed to achieve?

Jim

P.S.:
> You can't make it a constraint violation, because it can't always be
> detected, even in principle

Yep <blush> - I dropped a "not" in my original report.

---
[ 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: Steve Clamage <stephen.clamage@sun.com>
Date: 2000/05/11
Raw View
On 04 May 00 16:52:26 GMT, jthill@telus.net (Jim Hill) wrote:

>10.4 [class.abstract] p6:
>> Member functions can be called from a constructor (or destructor) of an
>> abstract class; the effect of making a virtual call (10.3) to a pure
>> virtual function directly or indirectly for the object being created (or
>> destroyed) from such a constructor (or destructor) is undefined.
>
>As it stands, a pure virtual cannot perform necessary (but insufficient)
>initialization that its overriders must complete: abstract base classes'
>initialization is forbidden to resemble (i.e. subset) valid intended
>behavior for its concrete implementations. I see no reason for this.

But you can call a pure virtual function if it has been defined -- you
just can't call it via the virtual function call syntax. You must
qualify the function name -- but to call a base-class version of a
virtual function, you have to qualify it anyway. So I don't see the
problem.

---
Steve Clamage, stephen.clamage@sun.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: jthill@telus.net (Jim Hill)
Date: 2000/05/13
Raw View
[ I'm resending this because I didn't get a robomoderator ack and it
  hasn't appeared. This isn't the first time. What's up? ]

Steve Clamage <stephen.clamage@sun.com> wrote:

> to call a base-class version of a virtual function, you have to
> qualify it anyway.
>
If this statement were true, there wouldn't be a problem.

But it's not true.  For "base-class" in the above quote read
"base-class-subobject", and reread 12.7p3.

Jim

---
[ 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: Steve Clamage <stephen.clamage@sun.com>
Date: 2000/05/16
Raw View
On Sat, 13 May 2000 02:54:31 CST, jthill@telus.net (Jim Hill) wrote:

>[ I'm resending this because I didn't get a robomoderator ack and it
>  hasn't appeared. This isn't the first time. What's up? ]
>
>Steve Clamage <stephen.clamage@sun.com> wrote:
>
>> to call a base-class version of a virtual function, you have to
>> qualify it anyway.
>>
>If this statement were true, there wouldn't be a problem.
>
>But it's not true.  For "base-class" in the above quote read
>"base-class-subobject", and reread 12.7p3.

Sorry, I don't see what you are getting at. Here's what I mean:

struct Base {
 virtual void f() { };
 virtual void g() = 0;
};
void Base::g() { } // implementation of pure virtual function

struct Derived : Base {
 void f();
 void g();
};

void Derived::f()
{
 Base::f(); // #1
 Base::g(); // #2
}

void Derived::g()
{
 Base::f(); // #3
 Base::g(); // #4
}

In all 4 cases, we can call a base-class version of a function
overridden in a derived class, pure virtual or not, by qualifying the
function name with the base class name.

What was your concern?


---
Steve Clamage, stephen.clamage@sun.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: look@sig.please.because.this.is.invalid.bhp.com.au (Chris Kuan)
Date: 2000/05/17
Raw View
stephen.clamage@sun.com (Steve Clamage) wrote in comp.std.c++:

>On Sat, 13 May 2000 02:54:31 CST, jthill@telus.net (Jim Hill) wrote:

>>Steve Clamage <stephen.clamage@sun.com> wrote:
>>
>>> to call a base-class version of a virtual function, you have to
>>> qualify it anyway.
>>>
>>If this statement were true, there wouldn't be a problem.
>>
>>But it's not true.  For "base-class" in the above quote read
>>"base-class-subobject", and reread 12.7p3.

> ... we can call a base-class version of a function
>overridden in a derived class, pure virtual or not, by qualifying the
>function name with the base class name.
>
>What was your concern?

AFAICS, Jim is objecting to the "undefinedness" in 10.4p6 on calling a pure
virtual virtually within a cdtor. He would prefer that it behave (as in 12.7p3)
as a "normal" virtual call (either that or just make it a constraint violation.)

--
Chris Kuan, BHP Information Technology
Concatenate for email: mr gazpacho @ hotmail . com

"Law is a repository for the aimlessly clever" - Tim Freedman

---
[ 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: jthill@telus.net (Jim Hill)
Date: 2000/05/04
Raw View
10.4 [class.abstract] p6:
> Member functions can be called from a constructor (or destructor) of an
> abstract class; the effect of making a virtual call (10.3) to a pure
> virtual function directly or indirectly for the object being created (or
> destroyed) from such a constructor (or destructor) is undefined.

As it stands, a pure virtual cannot perform necessary (but insufficient)
initialization that its overriders must complete: abstract base classes'
initialization is forbidden to resemble (i.e. subset) valid intended
behavior for its concrete implementations. I see no reason for this.

An implementation is required by 12.7p3 [class.cdtor] to call all other
virtual functions as defined for the class whose [de]constructor is
running; and a call to any undefined member function is already
undefined by 3.2 [basic.def.odr] and (afaics redundantly) by 10.3p8
[class.virtual] (see note 1 for detailed reasoning re 3.2 and 10.3p8).

The only other difference between the wording of 12.7p3/3.2/10.3p8 and
that of 10.4p6 is the absence of the phrase "no diagnostic [is]
required", which is itself redundant in all cases of explicitly
undefined behavior by 1.4p1 [intro.compliance] and 1.3.12
[defns.undefined].

I can imagine the paragraph might have intended to require a diagnostic
for pure-virtual calls from cdtors, but in that case it should have used
"shall" not "undefined", and it would then simply mandate a diagnostic
for easily detectable, useful and (otherwise) already-supported and
-defined behavior.

Therefore, all constructions I can conceive for 10.4p6 make it
redundant, or misworded and pointless.

Proposed resolution: delete 10.4p6; convert 10.3p8 to a note; and delete
the sentence of 10.4p2 that reads `A pure virtual function need be
defined only if explicitly called with the qualified-id syntax (5.1).`
(see note 2 for detailed reasoning re 10.4p2).

Here is a trivial(ized) example of the usage prohibited:
<CODE>
struct foo {
  foo(int);
  virtual void reset(int) = 0;
private:
  int origv;
};

struct food : foo {
  food(int);
  virtual void reset(int);
private:
  int v;
};

void
food::reset(int a) { v = a; }

food::food(int a) : foo(a)
                  { reset(a); }

void
foo::reset(int a) { origv = a; }

#if defined(FAILMISERABLY)
foo::foo(int a)   { reset(a); }      // undefined behavior?!
#else
foo::foo(int a)   { foo::reset(a); } // defined, just another edit to
                                     // remember when implementing.
#endif

int main()
{
  food s(4);
}
</CODE>
=====
Note 1: calls to undefined virtuals are already undefined per 3.2, and
10.3p8 is redundant.

  I can see two possible objections to this conclusion, namely that 3.2
doesn't explicitly say it, and that the language I believe forces the
conclusion doesn't apply to virtuals at all, let alone pure virtuals.

1.4p2, point 3 says
> If a program contains a violation of a rule for which no diagnostic is
> required, this International Standard places no requirement on
> implementations with respect to that program.

and 1.3p12 defines `undefined behavior` as
> behavior [...] for which this International Standard imposes no requirements.

Therefore, any violation of a rule for which no diagnostic is required
is by definition undefined behavior.

3.2p3:
> Every program shall contain exactly one definition of every non-inline
> function or object that is used in that program; no diagnostic required.
> The definition can appear explicitly in the program, it can be found in
> the standard or a user-defined library, or (when appropriate) it is
> implicitly defined (see 12.1, 12.4 and 12.8). An inline function shall
> be defined in every translation unit in which it is used.

3.2p3's use of "function" clearly includes "member function", despite
the fact that at least many uses of "function" outside 3.2 do not:
12.1, 12.4 and 12.8 define member functions.

Therefore, using a member function that is not defined produces
undefined behavior, by definition.

It remains to be shown that a pure virtual member function is "used"
when called.  3.2p2 states in part:
> An expression is potentially evaluated unless either it is the operand
> of the sizeof operator (5.3.3), or it is the operand of the typeid
> operator and does not designate an lvalue of polymorphic class type
> (5.2.8). An object or non-overloaded function is used if its name
> appears in a potentially-evaluated expression. A virtual member function
> is used if it is not pure. An overloaded function is used if it is
> selected by overload resolution when referred to from a
> potentially-evaluated expression. [Note: this covers calls to named
> functions (5.2.2), operator overloading (clause 13), user-defined
> conversions (12.3.2), allocation function for placement new (5.3.4), as
> well as non-default initialization (8.5). A copy constructor is used
> even if the call is actually elided by the implementation. ]

As with 3.2p3, 3.2p2's use of "function" clearly includes "member
function": if it did not, there would be no definition of "used" for
member functions, and the note here makes the intent plain by again
citing various categories of member function as referents of (plain)
"function".  The sole explicit reference to member functions in all of
3.2, "A virtual member function is used if it is not pure.", only
expands the conditions under which a non-pure virtual member function is
`used`; it does not limit the conditions under which any function or
object is `used`.

Therefore, *all* categories of function are `used` if (even only
potentially) called, including [[[pure] virtual] member] functions.

Therefore, any potential call to any undefined virtual function is
already undefined per 3.2, and

Therefore, 10.3p8:
> A virtual function declared in a class shall be defined, or declared
> pure (10.4) in that class, or both; but no diagnostic is required (3.2).
is redundant, and should be converted to a note.
Q.E.D.
=====
Note 2: The last sentence of 10.4p2

  The cited sentence reads "A pure virtual function need be defined only
if explicitly called with the qualified-id syntax (5.1)."

To the extent that it is not redundant with 3.2, it is redundant with
10.4p6: an explicit call is certainly a `use` and hence requires a
definition by 3.2, but the unqualified-id of a pure virtual function is
also a use of that function when a cdtor instance for its class is
active, per the name lookup rules in 10.2 and the requirements in
12.3p7.

In order to have any point at all, this sentence would have to be read
as a (so deeply implicit I'd call it "submarine") prohibition on
unqualified calls to pure virtuals, but that would only make it
redundant with 10.4p6.

=====
Note 3:

  Thanks to Fergus Henderson for requesting the clarifications that led
me to write Notes 1 and 2 and to identify the issues with 10.3p8 and
10.4p2.

Jim
--
Jim Hill <jthill@telus.net>
---
[ 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              ]