Topic: overriding constant member functions


Author: "James Kanze" <kanze.james@neuf.fr>
Date: Sun, 8 Oct 2006 13:14:36 CST
Raw View
"Kristof Zelechovski" wrote:
> Uzytkownik "Greg Herlihy" <greghe@pacbell.net> napisal w wiadomosci
> news:1160147636.738944.213430@c28g2000cwb.googlegroups.com...

    [...]
> >> However, we cannot do that because this->m_x would be
> >> inferred constant.

> > A method's const qualifier guarantees that calling the
> > method will not change the object's "value" or state. Now
> > assigning zero to one of the object's data members certainly
> > appears like a change is being made to the object's state.
> > Presumably, the value of m_x means something to the
> > implementation of CC and that a change to its value would be
> > detectable by the client. If that is the case, then foo() is
> > neither const nor does it override CI's abstract foo() const
> > method. The programmer needs to use a different function.

> >> I do not find this extension of constance justified.

> > But whoever designed CI's interface must feel otherwise. The foo method
> > would not be declared const unless the designer thought it is
> > reasonable to require that every derived class of CI, implement foo()
> > in a const way. Since we have no way of knowing what foo() is supposed
> > to do or what the data member m_x represents, we have no way of
> > evaluating whether this requirement seems at all reasonable or not. If
> > the const requirement is not reasonable than IC should declare the
> > method as non-const - it's not necessary to change the language to fix
> > that kind of problem.

> I think a feeling is too vague a criterion to decide whether a
> method of the interface should be constant or not.

I don't think that Greg was using the work "feeling" to
expression a vague sentiment.  In this case, it has a much
stronger meaning.  Basically, the author of the base class
decided that the contract for this function would be that it has
no effects on the logical value of the object.  And it's up to
the author of the base class to establish the contract.

> The client code is generally free to provide implementations
> for the vendor's interfaces but it is not allowed to change
> the interfaces so it is already too late when the client
> recognizes that he does not feel good about a constant
> implementation (every method can be made constant by using a
> constant pointer to the implementation but it is a dirty trick
> and the client need not like being forced to use it).

On the other hand, there is likely to be code that depends on
the fact that calling the function does not modify the
observable state.  Regardless of the const (i.e. in languages
which don't support const), if the contract says you don't
modify the observable state, you'd better not modify the
observable state if you want the code to work.

It just happens that in C++, we can make this part of the
contract part of the type system.

--
James

---
[ 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: giecrilj@stegny.2a.pl ("K i tof elechovski")
Date: Thu, 5 Oct 2006 20:01:23 GMT
Raw View
In the following code, CC remains an abstract base class because
IC::foo(void) const remains unimplemented:
class IC { virtual void foo() const = 0; }; class CC { int m_x; void foo()
{ this->m_x = 0; }};
In order for CC::foo to override IC::foo, it must be declared as void foo()
const.
However, we cannot do that because this->m_x would be inferred constant.
I do not find this extension of constance justified.
If CC::foo overrides IC::foo,
it must treat this as a pointer to a constant object of class IC, in that no
member of class IC may be modified;
on the other hand, it is unreasonable to prohibit it from modifying
noninherited members of class CC.
Generally speaking,
if a function takes a reference to a constant object,
it should be expected
that the function does not modify the members of the object that belong to
its _declared_ type;
it should not be inferred that all members of the actual type of the object
remain unmodified.
Such an inference is mandatory under the current standard.
Am I right or wrong?  Is this suggestion ontologically acceptable?
In case I am wrong,
I wonder whether declaring a constant abstract virtual member function
should be disparaged.
Especially when the function belongs to an interface class.
Otherwise the implementor of the interface has three choices:
declare his private data that must be modified as mutable (too permissive),
access them via an internal pointer
(remember the latest discussion about leaking modifiable objects in the
constructor?) (uses unnecessary memory)
or use const_cast in the overriding implementation (clumsy).
Neither solution seems to be good.  What do you think?
Chris


---
[ 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: manphiz@gmail.com (manphiz)
Date: Fri, 6 Oct 2006 14:33:53 GMT
Raw View
> In the following code, CC remains an abstract base class because
> IC::foo(void) const remains unimplemented:
> class IC { virtual void foo() const = 0; }; class CC { int m_x; void foo()
> { this->m_x = 0; }};
> In order for CC::foo to override IC::foo, it must be declared as void foo()
> const.
> However, we cannot do that because this->m_x would be inferred constant.

Declaring this->m_x as mutable is one of the choices, in case that you
have to modify it in a const member function.

---
[ 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: Greg <gregh@podbridge.com>
Date: Fri, 6 Oct 2006 10:56:36 CST
Raw View

On Oct 5, 1:01  pm, giecr...@stegny.2a.pl ("K  i  tof   elechovski") wrote:
> In the following code, CC remains an abstract base class because
> IC::foo(void) const remains unimplemented:
> class IC { virtual void foo() const = 0; }; class CC { int m_x; void foo()
> { this->m_x = 0; }};
> In order for CC::foo to override IC::foo, it must be declared as void foo()
> const.

I think a better approach for implementing CC::foo() const would be to write
a const method for it - rather then rename one of CC's non-const methods()
in an attempt to pass it off as CC::foo() const. Frankly, it is hard to see
how CC::foo()'s current implementation could be considered at all const.

> However, we cannot do that because this->m_x would be inferred constant.

A method's const qualifier guarantees that calling the method will not
change the object's "value" or state. Now assigning zero to one of the
object's data members certainly appears like a change is being made to the
object's state. Presumably, the value of m_x means something to the
implementation of CC and that a change to its value would be detectable by
the client. If that is the case, then foo() is neither const nor does it
override CI's abstract foo() const method. The programmer needs to use a
different function.

> I do not find this extension of constance justified.

But whoever designed CI's interface must feel otherwise. The foo method
would not be declared const unless the designer thought it is reasonable to
require that every derived class of CI, implement foo() in a const way.
Since we have no way of knowing what foo() is supposed to do or what the
data member m_x represents, we have no way of evaluating whether this
requirement seems at all reasonable or not. If the const requirement is not
reasonable than IC should declare the method as non-const - it's not
necessary to change the language to fix that kind of problem.

> If CC::foo overrides IC::foo,
> it must treat this as a pointer to a constant object of class IC, in that no
> member of class IC may be modified;
> on the other hand, it is unreasonable to prohibit it from modifying
> noninherited members of class CC.

Any change to any of the object's members is potentially a change to the
object's value - and therefore requires a non-const object. It's up to the
programmer, and not the language, to decide which members contribute to the
object's value and which do not. And it's far better for the language to
assume that all members are significant instead of just some of them. In
fact by what means would a programmer under these new rules recreate the
current behavior in which all members are treated alike when evaluating the
constness of an operation?

> Generally speaking,
> if a function takes a reference to a constant object,
> it should be expected
> that the function does not modify the members of the object that belong to
> its _declared_ type;

Declared where? And what members of an object would a function be modifying
if not the members of the object's declared type? What other members would
there be?

> it should not be inferred that all members of the actual type of the object
> remain unmodified.

An object has just one value - that is, just one state at any one time.
Granted not all clients of a polymorphic object may be able to observe its
state to the degee as other clients, but that is not to say that the object
ever has more than a single state.

> Such an inference is mandatory under the current standard.
> Am I right or wrong?   Is this suggestion ontologically acceptable?

It sounds as if the goal is for the object to hold multiple values - the
constness of each could then be individually adjustable. If that is the
goal, then inheritance is not the right pattern. Instead the object should
use composition - instead of inheriting from IC, CC would have an IC data
member. in that way the constness of the CC object and the constness of the
CC object could be maintained in a more autonomous manner.

Greg

---
[ 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: "Greg Herlihy" <greghe@pacbell.net>
Date: Fri, 6 Oct 2006 12:05:32 CST
Raw View

On Oct 5, 1:01 pm, giecr...@stegny.2a.pl ("K   i   tof    elechovski")
wrote:
> In the following code, CC remains an abstract base class because
> IC::foo(void) const remains unimplemented:
> class IC { virtual void foo() const = 0; }; class CC { int m_x; void foo()
> { this->m_x = 0; }};
> In order for CC::foo to override IC::foo, it must be declared as void foo()
> const.

I think a better approach for implementing CC::foo() const would be to
write a const method for it - rather then rename one of CC's non-const
methods() in an attempt to pass it off as CC::foo() const. Frankly, it
is hard to see how CC::foo()'s current implementation could be
considered at all const.

> However, we cannot do that because this->m_x would be inferred constant.

A method's const qualifier guarantees that calling the method will not
change the object's "value" or state. Now assigning zero to one of the
object's data members certainly appears like a change is being made to
the object's state. Presumably, the value of m_x means something to the
implementation of CC and that a change to its value would be detectable
by the client. If that is the case, then foo() is neither const nor
does it override CI's abstract foo() const method. The programmer needs
to use a different function.

> I do not find this extension of constance justified.

But whoever designed CI's interface must feel otherwise. The foo method
would not be declared const unless the designer thought it is
reasonable to require that every derived class of CI, implement foo()
in a const way. Since we have no way of knowing what foo() is supposed
to do or what the data member m_x represents, we have no way of
evaluating whether this requirement seems at all reasonable or not. If
the const requirement is not reasonable than IC should declare the
method as non-const - it's not necessary to change the language to fix
that kind of problem.

> If CC::foo overrides IC::foo,
> it must treat this as a pointer to a constant object of class IC, in that no
> member of class IC may be modified;
> on the other hand, it is unreasonable to prohibit it from modifying
> noninherited members of class CC.

Any change to any of the object's members is potentially a change to
the object's value - and therefore requires a non-const object. It's up
to the programmer, and not the language, to decide which members
contribute to the object's value and which do not. And it's far better
for the language to assume that all members are significant instead of
just some of them. In fact by what means would a programmer under these
new rules recreate the current behavior in which all members are
treated alike when evaluating the constness of an operation?

> Generally speaking,
> if a function takes a reference to a constant object,
> it should be expected
> that the function does not modify the members of the object that belong to
> its _declared_ type;

Declared where? And what members of an object would a function be
modifying if not the members of the object's declared type? What other
members would there be?

> it should not be inferred that all members of the actual type of the object
> remain unmodified.

An object has just one value - that is, just one state at any one time.
Granted not all clients of a polymorphic object may be able to observe
its state to the degee as other clients, but that is not to say that
the object ever has more than a single state.

> Such an inference is mandatory under the current standard.
> Am I right or wrong?  Is this suggestion ontologically acceptable?

It sounds as if the goal is for the object to hold multiple values -
the constness of each could then be individually adjustable. If that is
the goal, then inheritance is not the right pattern. Instead the object
should use composition - instead of inheriting from IC, CC would have
an IC data member. in that way the constness of the CC object and the
constness of the CC object could be maintained in a more autonomous
manner.

Greg


---
[ 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: giecrilj@stegny.2a.pl ("Kristof Zelechovski")
Date: Sat, 7 Oct 2006 11:23:27 GMT
Raw View
Uzytkownik "Greg Herlihy" <greghe@pacbell.net> napisal w wiadomosci=20
news:1160147636.738944.213430@c28g2000cwb.googlegroups.com...
>
>
> On Oct 5, 1:01 pm, giecr...@stegny.2a.pl ("K=F8i=B9tof =AEelechovski")
> wrote:
>> In the following code, CC remains an abstract base class because
>> IC::foo(void) const remains unimplemented:
>> class IC { virtual void foo() const =3D 0; }; class CC { int m_x; void=
=20
>> foo()
>> { this->m_x =3D 0; }};
>> In order for CC::foo to override IC::foo, it must be declared as void=20
>> foo()
>> const.
>
> I think a better approach for implementing CC::foo() const would be to
> write a const method for it - rather then rename one of CC's non-const
> methods() in an attempt to pass it off as CC::foo() const. Frankly, it
> is hard to see how CC::foo()'s current implementation could be
> considered at all const.
>
>> However, we cannot do that because this->m_x would be inferred constan=
t.
>
> A method's const qualifier guarantees that calling the method will not
> change the object's "value" or state. Now assigning zero to one of the
> object's data members certainly appears like a change is being made to
> the object's state. Presumably, the value of m_x means something to the
> implementation of CC and that a change to its value would be detectable
> by the client. If that is the case, then foo() is neither const nor
> does it override CI's abstract foo() const method. The programmer needs
> to use a different function.
>
>> I do not find this extension of constance justified.
>
> But whoever designed CI's interface must feel otherwise. The foo method
> would not be declared const unless the designer thought it is
> reasonable to require that every derived class of CI, implement foo()
> in a const way. Since we have no way of knowing what foo() is supposed
> to do or what the data member m_x represents, we have no way of
> evaluating whether this requirement seems at all reasonable or not. If
> the const requirement is not reasonable than IC should declare the
> method as non-const - it's not necessary to change the language to fix
> that kind of problem.
>

I think a feeling is too vague a criterion to decide whether a method of =
the=20
interface should be constant or not.
The client code is generally free to provide implementations for the=20
vendor's interfaces
but it is not allowed to change the interfaces so it is already too late=20
when the client recognizes
that he does not feel good about a constant implementation
(every method can be made constant by using a constant pointer to the=20
implementation
but it is a dirty trick and the client need not like being forced to use=20
it).

>> If CC::foo overrides IC::foo,
>> it must treat this as a pointer to a constant object of class IC, in t=
hat=20
>> no
>> member of class IC may be modified;
>> on the other hand, it is unreasonable to prohibit it from modifying
>> noninherited members of class CC.
>
> Any change to any of the object's members is potentially a change to
> the object's value - and therefore requires a non-const object. It's up
> to the programmer, and not the language, to decide which members
> contribute to the object's value and which do not. And it's far better
> for the language to assume that all members are significant instead of
> just some of them. In fact by what means would a programmer under these
> new rules recreate the current behavior in which all members are
> treated alike when evaluating the constness of an operation?
>

That rule would be observed unless the method is overriding.
If you want full constance of the actual object,
wrap the code in an original constant method and call the original method=
=20
from the overriding method.

>> Generally speaking,
>> if a function takes a reference to a constant object,
>> it should be expected
>> that the function does not modify the members of the object that belon=
g=20
>> to
>> its _declared_ type;
>
> Declared where? And what members of an object would a function be
> modifying if not the members of the object's declared type? What other
> members would there be?
>

Declared in the declaration of the function.
E.g. if a function foo takes a reference to a constant object of base typ=
e,
the members of the derived type as declared in the base type
are guaranteed not to change via the reference
(remember that the object referred to by a reference to a constant object=
=20
need not be constant itself
- you cannot assume that the observable state of an object does not chang=
e
just because you refer to it as to a constant object).

>> Such an inference is mandatory under the current standard.
>> Am I right or wrong?  Is this suggestion ontologically acceptable?
>
> It sounds as if the goal is for the object to hold multiple values -
> the constness of each could then be individually adjustable. If that is
> the goal, then inheritance is not the right pattern. Instead the object
> should use composition - instead of inheriting from IC, CC would have
> an IC data member. in that way the constness of the CC object and the
> constness of the CC object could be maintained in a more autonomous
> manner.
>

You cannot use composition in order to implement an interface, for two=20
reasons:
the interface is an abstract base class and you must use an implementatio=
n=20
to embed it,
which is just what you are trying to do,
and you cannot call the methods of the interface from client code unless =
you=20
inherit from it,
which would make such an "implementation" invisible for the server code.

Chris=20


---
[ 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                      ]