Topic: Accessibility of nested types


Author: smeyers@aristeia.com (Scott Meyers)
Date: 1999/04/21
Raw View
Okay, I feel better now that I've been reassured that private means
private.  Now let me change the example to add a template class that's
instantiated with a private type:

  template <typename T>
  class SomeClass {
  public:
    SomeClass() { p = new T; }    // 1
  private:
    T *p;
  };

  class Outer {
  private:
    struct Inner{};

    SomeClass<Inner> x;          // 2
  };

I believe the instantiation at line 2 is valid, because Outer has access to
Inner.  However, I believe the code at line 1 is invalid, because
SomeClass<Outer::Inner> has no access to Outer::Inner (which is what T
happens to be).  But perhaps there's some rule about template instantiation
that says that if a class can be instantiated, its member functions can,
too.  As before, my compilers like the code above, but I don't trust them.
Should the above compile?

Thanks,

Scott

PS - My reason for wanting to know this has to do with my treatment of
smart pointers and reference counting in More Effective C++.  Ever since I
wrote the book in 1995, the code for Item 29 has compiled and run just
fine, but I recently received a bug report telling me that my smart pointer
had no access to what it pointed to.  I now believe that's true, but it
seems strange that in four years, nobody has ever reported this error to me
before, so I'm proceeding with caution...

--
Scott Meyers, Ph.D.                  smeyers@aristeia.com
Software Development Consultant      http://www.aristeia.com/
Visit http://meyerscd.awl.com/ to demo the Effective C++ CD
---
[ 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: Valentin Bonnard <Bonnard.V@wanadoo.fr>
Date: 1999/04/21
Raw View
I think that you are confused.

Scott Meyers wrote:

> Okay, I feel better now that I've been reassured that private means
> private.

But the commitee might still change that in a TC... <g>
(no, we actually don't want to break encapsulation)

> Now let me change the example to add a template class that's
> instantiated with a private type:
>
>   template <typename T>
>   class SomeClass {
>   public:
>     SomeClass() { p = new T; }    // 1
>   private:
>     T *p;
>   };
>
>   class Outer {
>   private:
>     struct Inner{};
>
>     SomeClass<Inner> x;          // 2
>   };
>
> I believe the instantiation at line 2 is valid, because Outer has access to
> Inner.

Correct

> However, I believe the code at line 1 is invalid, because
> SomeClass<Outer::Inner> has no access to Outer::Inner (which is what T
> happens to be).

Code at line 1 has access to T ! That's all what is needed.

> But perhaps there's some rule about template instantiation
> that says that if a class can be instantiated, its member functions can,
> too.

???

> As before, my compilers like the code above, but I don't trust them.
> Should the above compile?

Yes. There is no problem.

Otherwise every template class would be broken. (Including, but
not limited too, class templates in the standard library.)

Let me write another example:

class MyAccount
{
private:
    long my_money;
public:
    long read_amount () const
    {
        return my_money;
    }
} account;

print (account.read_amount ());

void print (long value)
{
    cout << value; // cannot print value, since it refers to
                   // my_money, which is a private variable !
}

--

Valentin Bonnard
---
[ 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: smeyers@aristeia.com (Scott Meyers)
Date: 1999/04/21
Raw View
On 21 Apr 99 00:56:15 GMT, Valentin Bonnard wrote:
> I think that you are confused.

Hardly for the first time :-)

> > However, I believe the code at line 1 is invalid, because
> > SomeClass<Outer::Inner> has no access to Outer::Inner (which is what T
> > happens to be).
>
> Code at line 1 has access to T ! That's all what is needed.

This makes perfect sense.  Once I again I tripped up on the "templates are
not macros" problem.  Thanks for setting me straight -- and for
demonstrating that I apparently understood C++ better in 1995 than I do
now...

Scott

--
Scott Meyers, Ph.D.                  smeyers@aristeia.com
Software Development Consultant      http://www.aristeia.com/
Visit http://meyerscd.awl.com/ to demo the Effective C++ CD
---
[ 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: sbnaran@fermi.ceg.uiuc.edu (Siemel Naran)
Date: 1999/04/21
Raw View
On 21 Apr 99 00:41:23 GMT, Scott Meyers <smeyers@aristeia.com> wrote:

/*
>  template <typename T>
>  class SomeClass {
>  public:
>    SomeClass() { p = new T; }    // 1
>  private:
>    T *p;
>  };
*/

/* If this is ill-formed on the grounds that class T is inacessible,
   then the following useful code should not compile:
*/


#include <vector>
#include <numeric>
#include <iostream.h>

class Something
{
   private:
      struct Calculate; // ideally, should be local class of calculate()
      std::vector<int> things;
   public:
      Something(int a, int b) { things.push_back(a); things.push_back(b); }
      int calculate() const;
};

struct Something::Calculate
{
   int operator()(int running, int val) const { return running*val; }
};

int Something::calculate() const
{
   // ideally, class Calculate should be defined here as a local class
   return std::accumulate(things.begin(),things.end(),(int)1,Calculate());
      // std::accumulate does not have access to Something::Calculate
      // so should this fail?
      // NO, because it is a reasonable thing to do
}

int main()
{
   Something s(2,3);
   cout << s.calculate() << '\n'; // should print "6"
}


/* Both como and egcs accept the program, as expected.
   But I think an explicit instantiation should fail.
*/

int accumulate
   (int const * begin, int const *const end,
    int init, Something::Calculate calc)
{
   for ( ; begin!=end; ++begin) init=calc(init,*begin);
   return init;
}

/* Como rejects this function accumulate, but egcs accepts it.
   Here is the error message from como:

   "l.c", line 30: error: class "Something::Calculate" is inaccessible
      int init, Something::Calculate calc)
                           ^
*/

--
----------------------------------
Siemel B. Naran (sbnaran@uiuc.edu)
----------------------------------


[ 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: Francis Glassborow <francis@robinton.demon.co.uk>
Date: 1999/04/19
Raw View
In article <7etsgd$hr4$1@nnrp1.dejanews.com>, dhunter@lehman.com writes
>FYI Sparcworks 4.2 and 5.0 fail on all 4 lines, although 4.2 lists
>them as anachronisms which surprises me as I can't remember a time
>when private declarations of any type were publicly accessible.

A very long time ago we had the problem that C++ compatibility with C
resulted in nested classes being exported into global space.  That might
be the origin of such a warning.



Francis Glassborow      Journal Editor, Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA          +44(0)1865 246490
All opinions are mine and do not represent those of any organisation
---
[ 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: Richard Ashton <rashton@dircon.co.uk>
Date: 1999/04/13
Raw View
C++ Builder 4 and I agree with you.

Richard Ashton.
---
[ 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: sbnaran@localhost.localdomain (Siemel Naran)
Date: 1999/04/13
Raw View
On 12 Apr 99 06:33:00 GMT, Scott Meyers <smeyers@aristeia.com> wrote:

>Well, I thought I understood the basics, but now I'm not so sure.  Isn't it
>true that a private type is inaccessible outside a class?  For example,
>given this,

Yes, this is true.

But the outside world can use nested private types indirectly.  Eg,
   class Outer {
         class Inner { };
      public:
         explicit Outer(const Inner&);
         static Inner f() { return Inner(); }
   };
   int main() {
      Outer::Inner i=Outer::f(); // error: 'Outer::Inner' is private
      Outer o(Outer::f()); // ok
   }


>shouldn't lines 1-4 fail to compile?  I thought so, but ECGS accepts them
>all, and VC6SP2 accepts line 4 (though it rejects lines 1-3).  This seems
>really elementary to me, but if it is, two commonly-used compilers have it
>wrong.  Either that or I just don't understand the fundamentals.

But como has it right!
And it throws in two warnings too!!


[sbnaran@localhost] [/tmp] >> como -c nested.cc
Comeau C/C++ 4.2.38 (Oct 13 1998 03:37:37)
Copyright 1988-1998 Comeau Computing, Edison Design Group, Inc.

"nested.cc", line 10: error: class "Outer::Inner1" is inaccessible
        Outer::Inner1 a;                  // 1
               ^

"nested.cc", line 11: error: class "Outer::Inner1" is inaccessible
        void *pv = new Outer::Inner1;     // 2
                              ^

"nested.cc", line 10: warning: variable "a" was declared but never referenced
        Outer::Inner1 a;                  // 1
                      ^

"nested.cc", line 15: error: class "Outer::Inner2" is inaccessible
        Outer::Inner2 a(0);               // 3
               ^

"nested.cc", line 16: error: class "Outer::Inner2" is inaccessible
        void *pv = new Outer::Inner2(0);  // 4
                              ^

4 errors detected in the compilation of "nested.cc".


--
----------------------------------
Siemel B. Naran (sbnaran@uiuc.edu)
----------------------------------
---
[ 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: dhunter@lehman.com
Date: 1999/04/13
Raw View
FYI Sparcworks 4.2 and 5.0 fail on all 4 lines, although 4.2 lists
them as anachronisms which surprises me as I can't remember a time
when private declarations of any type were publicly accessible.

David

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own
---
[ 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: jcoffin@taeus.com (Jerry Coffin)
Date: 1999/04/13
Raw View
In article <MPG.117b31d26efd766f98969b@news.teleport.com>,
smeyers@aristeia.com says...
> Well, I thought I understood the basics, but now I'm not so sure.  Isn't it
> true that a private type is inaccessible outside a class?  For example,
> given this,
>
>   class Outer {
>   private:
>     struct Inner1 {};
>     struct Inner2 { Inner2(int); };
>   };
>
> isn't it true that no function outside Outer should be able to refer to the
> types Inner1 and Inner2?

Yes, it's true.

>  For example, in the code below,
>
>   int main()
>   {
>     {
>       Outer::Inner1 a;                  // 1
>       void *pv = new Outer::Inner1;     // 2
>     }
>
>     {
>       Outer::Inner2 a(0);               // 3
>       void *pv = new Outer::Inner2(0);  // 4
>     }
>
>     return 0;
>   }
>
> shouldn't lines 1-4 fail to compile?  I thought so, but ECGS accepts them
> all, and VC6SP2 accepts line 4 (though it rejects lines 1-3).  This seems
> really elementary to me, but if it is, two commonly-used compilers have it
> wrong.  Either that or I just don't understand the fundamentals.

If you're wrong, so are a LOT of the rest of us.  I believe the
problem you're seeing lies entirely with the compilers you're using,
not with your understanding of C++.  If I might, I'd suggest picking
up yet another compiler -- since you're apparently working under
Win32, you might want to pick up a copy of Intel's VTune.  While many
compilers include some sort of profiler, this is a package sold as a
profiler, but which just _happens_ to include excellent C and C++
compilers as well.  It also includes a FORTRAN compiler that reputedly
produces very good code, but I've never really used it, so I can't
comment on it (and if I could, it wouldn't be topical anyway).

Just FWIW, Intel's compiler is based on the well-known EDG front-end,
and appears to be MUCH closer to the standard than either MS or egcs.

For a few examples, it supports covariant return types.  It has more
complete support for bool -- under MS, bool is supported as a keyword,
but comparison operators still produce int's.  Under Intel's compiler,
they produce bool's as they should.  Under MS, if you don't explicitly
return a value from main, you get a warning.  Under Intel, this gets
treated as is you had returned 0.

And, of course, it handles private correctly -- for you example code,
it gives error messages for all four lines you marked:

Intel(R) C/C++ Compiler Version 4.0 99026
Copyright (C) 1985-1999 Intel Corporation.  All rights reserved.

private.cpp
private.cpp(9) : error #308: class "Outer::Inner1" is inaccessible
        Outer::Inner1 a;                  // 1
               ^

private.cpp(10) : error #308: class "Outer::Inner1" is inaccessible
        void *pv = new Outer::Inner1;     // 2
                              ^

private.cpp(14) : error #308: class "Outer::Inner2" is inaccessible
        Outer::Inner2 a(0);               // 3
               ^

private.cpp(15) : error #308: class "Outer::Inner2" is inaccessible
        void *pv = new Outer::Inner2(0);  // 4
                              ^

Along with the good points, there are also a few bad ones: first of
all, if you do full optimization, Intel's compiler can produce
excellent code, but does so _excruciatingly_ slowly -- especially with
projects that include large headers; I have one program that compiles
in about 15 seconds with MS, but takes around 5 minutes to compile
with Intel.

Second, the Intel compiler does NOT ship with a standard library --
it's intended to plug into MS' environment, and uses the VC++ standard
library.  Some of MS' standard library has been limited in certain
ways due to MS' inability to handle some constructs.  Even though
Intel's compiler can probably handle (some of) those constructs, you
don't get the benefit of a library that uses them.
---
[ 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: Martin von Loewis <loewis@informatik.hu-berlin.de>
Date: 1999/04/13
Raw View
smeyers@aristeia.com (Scott Meyers) writes:

> I thought so, but ECGS accepts them all

This is a known bug in egcs. Please have a look at

http://egcs.cygnus.com/bugs.html

Regards,
Martin
---
[ 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: aandrei@my-dejanews.com
Date: 1999/04/13
Raw View
In article <MPG.117b31d26efd766f98969b@news.teleport.com>,
  smeyers@aristeia.com (Scott Meyers) wrote:
> Well, I thought I understood the basics, but now I'm not so sure.  Isn't it
> true that a private type is inaccessible outside a class?

FYI --

Incredible, CodeWarrior Pro 4 accepts the code like a free lunch. That's the
third main compiler that got it wrong. I'm going to submit a bug report.

Andrei

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own
---
[ 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: James.Kanze@dresdner-bank.com
Date: 1999/04/13
Raw View
In article <7etsgd$hr4$1@nnrp1.dejanews.com>,
  dhunter@lehman.com wrote:
> FYI Sparcworks 4.2 and 5.0 fail on all 4 lines, although 4.2 lists
> them as anachronisms which surprises me as I can't remember a time
> when private declarations of any type were publicly accessible.

CFront 2.1, at least, and 3.x too, I think, although in both cases, I
think it was considered a bug in CFront.  (Some of the 4.2 anachronisms
are bug-compatibility issues, I think.)

Before CFront 2.1, of course, a nested class was not just publicly
visible, it was exactly as if it had been declared at the file level
(which is still the case for nested struct's in C, I think).  And even
in CFront 2.1, if you referenced the nested class X::Y as simply Y, the
compiler would accept it with a warning.  This backwards compatibility
problem may have had something to do with the lack of access checking.

--
James Kanze                         mailto: James.Kanze@dresdner-bank.com
Conseils en informatique orient   e objet/
                        Beratung in objekt orientierter Datenverarbeitung
Ziegelh   ttenweg 17a, 60598 Frankfurt, Germany  Tel. +49 (069) 63 19 86 27

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own
---
[ 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: clamage@eng.sun.com (Steve Clamage)
Date: 1999/04/13
Raw View
dhunter@lehman.com writes:

>FYI Sparcworks 4.2 and 5.0 fail on all 4 lines, although 4.2 lists
>them as anachronisms which surprises me as I can't remember a time
>when private declarations of any type were publicly accessible.

The original cfront compilers were very lax about enforcing access.
When Sun stopped using cfront-based compilers, it arranged for
the new compilers to be more or less bug-compatible with cfront,
adding "anachronism" warnings. With the 5.0 release of Sun C++,
these warnings are now errors.

--
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: smeyers@aristeia.com (Scott Meyers)
Date: 1999/04/12
Raw View
Well, I thought I understood the basics, but now I'm not so sure.  Isn't it
true that a private type is inaccessible outside a class?  For example,
given this,

  class Outer {
  private:
    struct Inner1 {};
    struct Inner2 { Inner2(int); };
  };

isn't it true that no function outside Outer should be able to refer to the
types Inner1 and Inner2?  For example, in the code below,

  int main()
  {
    {
      Outer::Inner1 a;                  // 1
      void *pv = new Outer::Inner1;     // 2
    }

    {
      Outer::Inner2 a(0);               // 3
      void *pv = new Outer::Inner2(0);  // 4
    }

    return 0;
  }

shouldn't lines 1-4 fail to compile?  I thought so, but ECGS accepts them
all, and VC6SP2 accepts line 4 (though it rejects lines 1-3).  This seems
really elementary to me, but if it is, two commonly-used compilers have it
wrong.  Either that or I just don't understand the fundamentals.

Thanks,

Scott

--
Scott Meyers, Ph.D.                  smeyers@aristeia.com
Software Development Consultant      http://www.aristeia.com/
Visit http://meyerscd.awl.com/ to demo the Effective C++ CD
---
[ 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              ]