Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Friendly Nesting


October, 2004: Friendly Nesting

Herb Sutter (http://www.gotw.ca/) is a leading authority and trainer on C++ software development. Jim Hyslop is a senior software designer for Leitch Technology International. He can be reached at [email protected].


Aw, who broke the build?" I muttered. The code I had just checked out didn't compile, complaining about trying to access a private member. I opened the offending file and had a look. A quick look at the header to see who checked it in and...

"Uh, Wendy," I called out, "got a second?"

"Sure, 'sup pardner?" she said as she came into my cubicle.

"You, ah, broke the build," I said. "You're trying to access a private member. Have a look," I said as I found the line with the error. Simplified, the code boiled down to this:

class SomeClass
{
  void f();
  class Helper
  {
  public:
    void Help( SomeClass & s ) 
    {
       s.f(); 
    }
  };
};

"There it is," I pointed at the s.f() statement. "f() is a private member of SomeClass."

"Hmmm...yeah, but it compiled okay for me. I figured since Helper is a nested class, it automatically had access to private members of SomeClass." We both jumped at a loud snap behind us.

"And that, my daughter, is the problem," the Guru said as she tucked her tome under her arm. "You did not check your assumptions against the blessed Standard." I turned to my computer and called up my copy of the Standard. After a moment of searching, I found it:

"The members of a nested class have no special access to members of an enclosing class, nor to classes or functions that have granted friendship to an enclosing class; the usual access rules (clause 11) shall be obeyed. The members of an enclosing class have no special access to members of a nested class; the usual access rules (clause 11) shall be obeyed."[1]

"Ah, well, we'll just make it a friend, and that should do it," she reached over and modified the code:

 class SomeClass
 {
   void f();
   class Helper;
   friend class Helper;
   class Helper
 ... etc.

She turned and smiled at the Guru. The Guru said nothing, but smiled enigmatically back. Wendy's smile faded.

"I think you made another assumption, pardner," I murmured. Wendy switched back to the Standard, scanned it for a moment, and paused. And paused. I was starting not to like the pause. As I eased back from her, the air suddenly turned blue. While Wendy was purging the steam from her system, I peeked at the screen:

"A friend of a class is a function or class that is not a member of the class..."[2]

"Not a member of the class?!?" I blurted. "Well, there's gotta be some way to do this!" I turned back to the Guru.

"Wellll," the Guru drawled, momentarily dropping her shtick, "actually, there isn't any way a Standards-conforming program can grant a nested class access to the members of the enclosing class. It's a bit of an oversight, really. The early drafts of the Standard allowed access, but with various refinements in the Standard the access disappeared, and nobody noticed until after the Standard was adopted."

"Ah, well, so what do we do?" I asked.

"Well, there are a few options. First, you can move the inner classes to the same scope as the containing class," she wrote on the whiteboard:

class SomeClass
{
  friend class Helper;
  void f();
};
class Helper
{
public:
  void Help( SomeClass & s ) 
  {
     s.f(); 
  }
};

Just then, Bob came into sight, on his way to the kitchen for a fresh latte. The Guru transformed back into her character, and continued. "Alas, my children, this means that Helper is now accessible from global scope."

"Ick," I made a face. "Anything better?"

The Guru paused dramatically. "We can use Wendy's suggestion, and declare the nested class a friend."

"But you said..." Wendy protested.

"Peace, child," the Guru interrupted. "Sound engineering always trades off the theoretical against the practical. In this case, most compiler vendors recognized the oversight, and will let you declare the nested classes as friends. Some compilers, such as the one you use, my daughter, assume that nested classes are implicitly friends of the containing class."

"Right, then," I sighed, "so what's the future going to be? Surely the Standards committee won't let this continue?"

"No, my child, indeed the blessed disciples and prophets on the Committee have proposed an amendment that will grant nested classes access to all members of the containing class. Chapter 11 verse 1 will be modified to add: 'A member of a class can also access all names as the class of which it is a member'[3]. In our parable, this means that Helper can access all names that class SomeClass can access. Futhermore, this access is transitive—any class to which the containing class has access will also be accessible to the nested class." I gave her my tried-and-true deer-in-the-headlights. The Guru picked up the marker, and wrote on the whiteboard:

class T
{
  friend class SomeClass;
  void f();
};

class SomeClass
{
  class Helper
  {
  public:
    void f( T & t ) { t.f(); }
  };
};

"In this parable, class T has declared SomeClass to be a friend. This allows all members of SomeClass to access all members of class T. With the proposed amendment, since class Helper is a member of SomeClass, that means all members of class Helper can access all members of class T. Think of class Helper as a member of SomeClass's staff, with same access privileges as SomeClass. In other words, the parable lives a pious life, and is well formed."

"Whoa, whoa," I said. "Just a minute. If I'm the author of class T, I want to have a say in who accesses my classes. I gave SomeClass access, not the class Helper."

"Ah, my child, but meditate upon this. Helper is merely an implementation detail of SomeClass, is it not?" I grudgingly nodded. "Now therefore, class T should not care a whit which implementation detail is accessing its members—a function or a nested class. In this context, class Helper is acting on behalf of class SomeClass.

"This transitivity also applies if T is a nested class within SomeClass:"

class SomeClass
{
  void f();
  class FirstHelper
  {
  friend SomeClass;
  // ... etc.
  };
  class SecondHelper
  {
  };
};

"In this parable, because FirstHelper has declared SomeClass to be a friend, that means that class SecondHelper also has access to all members of FirstHelper."

"How far does this transitivity extend?" Wendy asked. "What if Helper itself has a nested class, like this:"

class SomeClass
 {
   void f();
   class Helper
   {
     class InnerHelper
    {
 ... etc.

"The transitivity is complete, my child. InnerHelper is a member of Helper, and as such has access to all names to which Helper has access. Helper, as we have discussed, is a member of SomeClass, and has access to all names to which SomeClass has access. Therefore, Helper::InnerHelper also has access to all members of SomeClass, and to all members of all classes that have declared SomeClass to be a friend.

"There is one last aspect to this," the Guru said. "The amendment to the Standard also states 'a local class of a member function may access the same names that the member function itself may access.' This will greatly ease implementation of local classes. Meditate upon this parable:"

class T
{
  void Acquire();
  void Release();
  friend class SomeClass;
};

class SomeClass
{
  void f();
};

void SomeClass::f()
{
  class ResourceManager
  {
    T & t_;
  public:
    ResourceManager( T & t ) 
    : t_( t ) 
    {
      t_.Acquire();
    }
    ~ResourceManager()
    {
      t_.Release();
    }
  };
}

"In the current revision of the Holy Standard, ResourceManager cannot access any members of SomeClass, let alone members of T. With the proposed amendment, the above parable lives a pious life." The Guru reopened her tome, and gazed off into space for a moment.

"It really is remarkable how closely friendship in C++ models friendship in real life. I allow my friends to drive my car. I decide who my friends are. Bob cannot say to me, 'I am your friend, give me your car keys.' Friendship is not inherited. You are my friend, but that does not mean your children will be my friends. You will only be my children's friend if they decide you are their friend. Your children are not automatically my friends. They may not borrow my car, unless you are driving. Friendship is not transitive. Your friends may not drive my car. I'm sure they are fine people—they are your friends, after all—but I don't know them. " She turned and glided away. "Once the Standard is revised, I will simply have to say 'you and your household staff'." Her voice trailed off as she disappeared around the corner.

Further Reading

"Member Access Control—Proposed Revisions," by Martin O'Riordan; ISO WG21 working document, http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/papers/2000/N1254.pdf. See also "Member Access Control and Nested Classes," by Alan Griffiths and Mark Radford, ISOWG21 working document, http://ra.dkung.dk/jtcl/sc22/wg21/docs/papers/2000/n1268.pdf.

References

[1] ISO/IEC 14822:2003(E), 11.8 6 1, available online from ANSI, http://webstore.ansi.org/.

[2] Ibid, 11.4 6 1.

[3] C++ Standard Core Language Issue Defect Report #45, http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#45.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.