Topic: Where next for Standard C++? induced const


Author: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1997/12/02
Raw View
One thing I would like is to have an "induced const" storage modifier in
classes:

struct A
{
  const? int* a;
    // points to const int for const A objects, to int for non-const A
objects
  A(): a(new int[20]);
  ~A() { delete a; }
};

int main()
{
  A a;
  const A ca;
  a.a[5]=7; // Ok: a not const => a.a does point to non-const int array
  ca.a[5]=7; // Error: ca const => ca.a points to const int array
}

Any comments?
(The syntax above is of course ad-hoc)
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: "Gabor Greif" <gabor@datawatch.de>
Date: 1997/12/02
Raw View
On Tue, Dec 2, 1997 11:16 AM, Christopher Eltschka
<mailto:celtschk@physik.tu-muenchen.de> wrote:
>One thing I would like is to have an "induced const" storage modifier in
>classes:
>
>struct A
>{
>  const? int* a;
>    // points to const int for const A objects, to int for non-const A
>objects
>  A(): a(new int[20]);
>  ~A() { delete a; }
>};
>
>int main()
>{
>  A a;
>  const A ca;
>  a.a[5]=7; // Ok: a not const => a.a does point to non-const int array
>  ca.a[5]=7; // Error: ca const => ca.a points to const int array
>}
>
>Any comments?
>(The syntax above is of course ad-hoc)

This is the typical case where you should use the encapsulation features of
C++,
i.e. use accessor functions that can differentiate on the constness of A
resulting in the correct c/v modifier in the result type. Exhibiting data
members directly to clients is a wrong paradigm anyway.

Look into the STL for examples (const_iterator).

My comment...

 Gabor
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Fergus Henderson <fjh@cs.mu.oz.au>
Date: 1997/12/02
Raw View
"Gabor Greif" <gabor@datawatch.de> writes:

>Christopher Eltschka wrote:
>>One thing I would like is to have an "induced const" storage modifier in
>>classes:
>>
>>struct A
>>{
>>  const? int* a;
>>  // points to const int for const A objects, to int for non-const A objects
>>  A(): a(new int[20]);
>>  ~A() { delete a; }
>>};
>>
>>int main()
>>{
>>  A a;
>>  const A ca;
>>  a.a[5]=7; // Ok: a not const => a.a does point to non-const int array
>>  ca.a[5]=7; // Error: ca const => ca.a points to const int array
>>}
>
>This is the typical case where you should use the encapsulation features of
>C++, i.e. use accessor functions that can differentiate on the constness of A
>resulting in the correct c/v modifier in the result type. Exhibiting data
>members directly to clients is a wrong paradigm anyway.

Encapsulation only protects clients of a class, whereas `const'
provides protection everywhere, even in methods of the class itself.

"Induced const" would allow you to declare what you want in one
place.  If you're only using encapsulation to enforce this,
then it needs to be said (and needs to be consistent) for
every member function that accesses the relevant data member.
This makes it more difficult get right in the first place,
and much more difficult to verify.

--
Fergus Henderson <fjh@cs.mu.oz.au>   |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>   |  of excellence is a lethal habit"
PGP: finger fjh@128.250.37.3         |     -- the last words of T. S. Garp.
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Oleg Zabluda <zabluda@math.psu.edu>
Date: 1997/12/03
Raw View
Christopher Eltschka <celtschk@physik.tu-muenchen.de> wrote:
: One thing I would like is to have an "induced const" storage modifier in
: classes:

: struct A
: {
:   const? int* a;
:     // points to const int for const A objects, to int for non-const A
: objects
:   A(): a(new int[20]);
:   ~A() { delete a; }
: };

: int main()
: {
:   A a;
:   const A ca;
:   a.a[5]=7; // Ok: a not const => a.a does point to non-const int array
:   ca.a[5]=7; // Error: ca const => ca.a points to const int array
: }

: Any comments?
: (The syntax above is of course ad-hoc)

Wrap your pointers in a class, for example like below, and it'll
happen automatically:

struct A
{
  vector<int> a;
    // points to const vector<int> for const A objects, to vector<int>
    // for non-const A
objects
  A(): a(20);
  ~A() {}
};

Also note how simpler and safer the implementation became.

Oleg.
--
Life is a sexually transmitted, 100% lethal disease.
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1997/12/03
Raw View
Gabor Greif wrote:
>
> On Tue, Dec 2, 1997 11:16 AM, Christopher Eltschka
> <mailto:celtschk@physik.tu-muenchen.de> wrote:
> >One thing I would like is to have an "induced const" storage modifier in
> >classes:
> >
> >struct A
> >{
> >  const? int* a;
> >    // points to const int for const A objects, to int for non-const A
> >objects
> >  A(): a(new int[20]);
> >  ~A() { delete a; }
> >};
> >
> >int main()
> >{
> >  A a;
> >  const A ca;
> >  a.a[5]=7; // Ok: a not const => a.a does point to non-const int array
> >  ca.a[5]=7; // Error: ca const => ca.a points to const int array
> >}
> >
> >Any comments?
> >(The syntax above is of course ad-hoc)
>
> This is the typical case where you should use the encapsulation features of
> C++,
> i.e. use accessor functions that can differentiate on the constness of A
> resulting in the correct c/v modifier in the result type. Exhibiting data
> members directly to clients is a wrong paradigm anyway.
>
> Look into the STL for examples (const_iterator).
>
> My comment...

The goal wasn't to make public pointers const; it was just easier to
demonstrate what I mean (that is, less work for the example). Don't
expect feature-explaning examples to be examples of good programming
practice.

To satisfy your objections, here an example which doesn't expose data
members:

template<typename T> class Array
{
  int size;
  const? T* p;
public:
  Array(int n): p(new T[n]) {}
  // ...
  T& operator[](int n) const // oops! What was meant is of course const
T& or just T
  {
    if(n>=0 && n<size)
      return p[n];           // I don't want this line to compile
    throw index_out_of_range(n, size);
  }
  T& operator[](int n)       // Ok: non-const method returns non-const ref
  {
    if(n>=0 && n<size)
      return p[n];           // I want this to compile, of course
    throw index_out_of_range(n, size);
  }
  // ...
};

Now, currently, there's no way to tell the compiler that I don't want
to change what b points to in const methods. The "inferred const"
feature would allow me to do just this, at the cost of either an
additional keyword (f. ex.  constable) or an additional construct (such
as my const? above). This would be the other side of logical constness
support (the one side being the mutable keyword).
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: david@unico.com.au (David Goh)
Date: 1997/12/04
Raw View
In comp.std.c++, on 02 Dec 97 09:16:53 GMT
Christopher Eltschka <celtschk@physik.tu-muenchen.de> wrote:
>One thing I would like is to have an "induced const" storage modifier in
>classes:
>
>struct A
>{
>  const? int* a;
>    // points to const int for const A objects, to int for non-const A
>objects
>  A(): a(new int[20]);
>  ~A() { delete a; }
>};
>
>int main()
>{
>  A a;
>  const A ca;
>  a.a[5]=7; // Ok: a not const => a.a does point to non-const int array
>  ca.a[5]=7; // Error: ca const => ca.a points to const int array
>}
>
>Any comments?
>(The syntax above is of course ad-hoc)

Mm.  You're exposing the data of the class directly.

This is, in general, a bad idea.
Try:

class A
{
public:
  A() : a(new int[20]);
  ~A() { delete[] a; }
  int & operator[](const size_t index)
    { return a[index]; }
  const int & operator[](const size_t index) const
    { return a[index]; }
private:
  int * a;
};

int main()
{
  A a;
  const A ca;
  a[5] = 7; // Fine, invokes non-const operator[] and returns valid lvalue.
  ca[5] = 7; // Won't work, const operator[] version invoked
             // and that returns a const reference.
}

Of course, you could then also do things like throw exceptions if the
passed in index value is out of range, etc etc.

Operator overloading is a good thing.  *ponder* See Scott Meyers
"Effective C++" book, chapters 20 and 21 for more about why *not* to
expose data members in the public interface, and when and how const
should be used.

If you don't have that book, and the next one, "More Effective C++", I
suggest you get them both, and read them cover to cover. :)

Later,

   David

--
Unico   Computer   Systems   Pty   Ltd
David Goh            Software Engineer
david@unico.com.au      (03) 9866 5688
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1997/12/05
Raw View
David Goh wrote:
>
> In comp.std.c++, on 02 Dec 97 09:16:53 GMT
> Christopher Eltschka <celtschk@physik.tu-muenchen.de> wrote:
> >One thing I would like is to have an "induced const" storage modifier in
> >classes:
> >
> >struct A
> >{
> >  const? int* a;
> >    // points to const int for const A objects, to int for non-const A
> >objects
> >  A(): a(new int[20]);
> >  ~A() { delete a; }
> >};
> >
> >int main()
> >{
> >  A a;
> >  const A ca;
> >  a.a[5]=7; // Ok: a not const => a.a does point to non-const int array
> >  ca.a[5]=7; // Error: ca const => ca.a points to const int array
> >}
> >
> >Any comments?
> >(The syntax above is of course ad-hoc)
>
> Mm.  You're exposing the data of the class directly.

Yes, because this way it was easier to write an explanative example for
the feature. This has nothing to do with the suggested feature itself
(or the reason why I would like to have it)

>
> This is, in general, a bad idea.

Yes, of course. But this was no example of good programming, but an
example to explain the suggested feature.  Look at the draft, and cound
how many examples there are bad programming practise. And then think
about why the draft contains such examples.

> Try:
>
> class A
> {
> public:
>   A() : a(new int[20]);
>   ~A() { delete[] a; }
>   int & operator[](const size_t index)
>     { return a[index]; }
>   const int & operator[](const size_t index) const
>     { return a[index]; }

And at this point, if I forgot the const of the return value, the
compiler won't tell me, leaving me with a seemingly const-correct class
and a possibility for really subtle bugs.

> private:
>   int * a;

If I could declare this const? int* a (with whatever syntax to choose),
the compiler would catch my possible error above (binding of const
value to non-const reference).

> };
>
> int main()
> {
>   A a;
>   const A ca;
>   a[5] = 7; // Fine, invokes non-const operator[] and returns valid lvalue.
>   ca[5] = 7; // Won't work, const operator[] version invoked
>              // and that returns a const reference.
> }

Yes, if I made it correctly (as you did above). But if I made this very
error I said above (and who never makes errors, no matter how stupid
they may be?), this would compile, and therefore, my previous error
(which went without diagnostic) causes this error not to be caught as
well.

Now you could say I can forget the const? as well, but there's only one
a, but there are usually many member functions accessing a. Thus the
error is much more likely to occur if deferred to member functions.

Another error that would be caught:

class A
{
  int* a;
public:
  // ...
  bool equal(int i, int j) const
  {
    return a[i]=a[j]; // A changed logically in const method!
                      // (Of course this should read a[i]==a[j])
  }
};

>
> Of course, you could then also do things like throw exceptions if the
> passed in index value is out of range, etc etc.
>

Of course, but this has nothing to do with induced const.

[...]
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1997/12/05
Raw View
Oleg Zabluda wrote:
>
> Christopher Eltschka <celtschk@physik.tu-muenchen.de> wrote:
> : One thing I would like is to have an "induced const" storage modifier in
> : classes:
>
> : struct A
> : {
> :   const? int* a;
> :     // points to const int for const A objects, to int for non-const A
> : objects
> :   A(): a(new int[20]);
> :   ~A() { delete a; }
> : };
>
> : int main()
> : {
> :   A a;
> :   const A ca;
> :   a.a[5]=7; // Ok: a not const => a.a does point to non-const int array
> :   ca.a[5]=7; // Error: ca const => ca.a points to const int array
> : }
>
> : Any comments?
> : (The syntax above is of course ad-hoc)
>
> Wrap your pointers in a class, for example like below, and it'll
> happen automatically:
>
> struct A
> {
>   vector<int> a;
>     // points to const vector<int> for const A objects, to vector<int>
>     // for non-const A
> objects
>   A(): a(20);
>   ~A() {}
> };
>
> Also note how simpler and safer the implementation became.

Good point - but not always it's just a pointer that should be const.
In general, you'll have to write a wrapper for every type where you want
a type used for its construction to be "induced const". Not very
satisfying.
(And, of course, every new object means another source of bugs.)

BTW, I might have a good reason not to use vector (f.ex. if I don't get
the data with new, but f.ex. as preinitialized table from an OS call).
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]