views:

330

answers:

2

Is following code legal C++ or not?

class Foo
{
  class Bar;

  void HaveADrink(Bar &bar);
  void PayForDrinks(Bar &bar);

  public:
  void VisitABar(int drinks);
};

class Foo::Bar
{
  public:
  int countDrinks;
};

void Foo::HaveADrink(Bar &bar)
{
  bar.countDrinks++;
}
void Foo::PayForDrinks(Bar &bar)
{
  bar.countDrinks = 0;
}
void Foo::VisitABar(int drinks)
{
  Bar bar;
  for (int i=0; i<drinks; i++) HaveADrink(bar);
  PayForDrinks(bar);
}

Both Visual C++ and GCC accepts it, however the code seems somewhat strange to me and I would hate to have it refused by some future compiler.

Still, the pattern seems useful to me to reduce compile time dependencies - I often use it to declare structs which are used to pass some "context" (a bunch of variables) which are shared between a few functions which all reside in the same cpp file, and this way I do not have to introduce the "context" definition into the public interface.

+3  A: 

legal, and indeed usefull to hide implementation details to the outside world.

stijn
+5  A: 

This is the "pimpl idiom" : http://c2.com/cgi/wiki?PimplIdiom

You're "forwarding" class Bar inside of class Foo. Perfectly legal as long as you don't do anything inside the definigino of Foo that would require the sizeof Bar. You can reference Bar using pointer or reference (Bar* or Bar&), but if you declare a data member in Foo such as this:

private: Bar _bar;

It wouldn't work. The reason is because the definition of Foo must be enough to determine the sizeof Foo. Since the size of Bar is unknown inside the definition of Foo, it would make the size of Foo indeterminate. But using a pointer would work:

private: Bar* _bar;

Because the sizeof of pointer is the same, and thus known, regardless of how Bar will be later defined.

zumalifeguard
Well, it's not _exactly_ the pimpl idiom (there's no pimpl in his code), but you're right - essentially it is. +1
sbi
It's only related to the pimpl, since it is more about the scope of some context, where the pimpl is the complete implementation of your class. But you're right: you can only declare a pointer when doing this. Do yourself a favour and use the appropriate smart pointer.
stefaanv
It's definately not PIMPL. There is no implementation in Bar.
Martin York
It is often used in combination with the Pimpl, declaring a "Impl" class which will be used for implementation, but it has a wider scope of application (contexts as mentioned for example).
Matthieu M.
While this might not be the typical pimpl implementation, it does seem to me to be essentially the same. The only difference from a normal pimpl idiom is that `Bar` isn't used to maintain state across different public method calls. But if the class doesn't need to have that state maintained over that span, there's no reason to keep it around so there's no reason to dynamically allocate it on the heap.
Michael Burr