c++ice

VS2015 Update 1 bug, or bad C++: Why can't a friend class access its friend's protected destructor?


The following appears to be a pattern employed by ZeroC ICE in the code it auto-generates, which appears to me to be a way they have made singletons (not sure why) for many releases of their tool now. Various compilers have no problem with it, until I found today that Visual Studio 2015 Update 1 (VS version 14.0.24720.00, VC++ version 19.00.23506) emits an error. Before Update 1, VS2015 also had no problem with it. I'm not sure whether it's a bug (regression?) in the VS2015 C++ compiler with Update 1, or bad (not standards-conformant) C++ code that other compilers let slide.

Here is an example of the code pattern:

class Foo {
protected:
    virtual ~Foo() {}

    friend class Foo_init;
};

class Foo_init {
public:
    Foo init;
};

static Foo_init staticFooInit;

VS2015 Update 1 emits these errors:

example.cpp(13): error C2248: 'Foo::~Foo': cannot access protected member declared in class 'Foo'
example.cpp(3): note: see declaration of 'Foo::~Foo'
example.cpp(1): note: see declaration of 'Foo'

I found one (as yet unanswered) ZeroC ICE forum post which seems related to this, but otherwise I haven't found out in my Google searching anything that convinces me whether this is a compiler issue or bad code. I admit I don't know ZeroC ICE very well, nor do I use C++ friend classes enough to have a deep understanding of what you can and can't do with them. I'm hoping someone more knowledgeable can shed some light on it.


Solution

  • I am not 100% sure on your exact problem, but it reminds me of a problem I had a while ago, where forward declared classes would have an unexpected scope. this page cppreference class highlights the rules, that a forward-declared class has the most local scope. However, your example on my VS2015u3 does not fail either.

    I think the fix is probably to forward declare the class which is a friend before the class, so that it has a well defined scope.

    When you have a class such as

    class Example {
         int someFunction( class SomeOtherClass & param );
    };
    

    The compiler treats declaration of SomeOtherClass which is within the local scope.

    This means that

    class Example {
         int someFunction( class SomeOtherClass & param );
    };
    
    class SomeOtherClass {
              ...
    };
    

    Declares three classes Example Example::SomeOtherClass and SomeOtherClass

    Changing your example to

    class Foo_init;
    
    class Foo {
      protected:
        virtual ~Foo() {}
    
        friend Foo_init;
     };
    
    class Foo_init {
      public:
        Foo init;
     };
    
     static Foo_init staticFooInit;
    

    Should work