Inner class, pimpl and a friend class - disagreeing compilers
Asked Answered
P

1

6

I was mucking about in some old library code, with the basic objective of refactoring it. This old code does not exactly comply to best practices and beauty (yes - friends are bad, and it has been removed after discovering the below - as it was an oversight in the refactoring).

Now preparing to run some unit tests I compiled the code with clang++, g++, and vc++ (2005 - yes I know it's old, but for backward compatibility - I have to).

g++ and clang++ compiled and ran without errors, but Visual C++ was complaining, so after looking at the code, i found something to the effect of this:

#include <iostream>

class one {

  private:
    struct private_impl;
    private_impl* pimpl_;

  public:
    one();
    ~one();
    void say_hello();
};

class two {

  private:
    friend class one;
    void say_world();

  public:

};

struct one::private_impl {
  two t;
  void say_world();
};

void one::private_impl::say_world() {
   std::cout << " ";
   t.say_world();  //This should not work should it?
}

one::one() : pimpl_(new private_impl) { }

one::~one() {
  delete pimpl_;
}

void one::say_hello() {
  std::cout << "Hello";
  pimpl_->say_world();
  std::cout << std::endl;
}

void two::say_world() {
  std::cout << "World";
}

int main() {
  one test;
  test.say_hello();
  return 0;
}

Compiled with switches (g++ and clang++):

-Wall -Wextra -pedantic

clang++ version: 3.3
g++ version:     4.8.2

Now Visual C++ complains that private_impl::say_world() cannot access the private member of the class two. To me after looking over the c++ friend rules, this is correct - but is my understanding of this wrong? Am I misreading the information (English is not my first language)?

From the standard (c++03 - do not have c++11 at hand where I'm at now):

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.

And also this:

Friendship is neither inherited nor transitive.

So my basic question here - who is actually correct - clang and gcc or vc++?

Also - this question is just for curiosity about the matter, and trying to understand the world a little better.

Percolation answered 15/1, 2014 at 10:2 Comment(4)
Interestingly, this has changed in C++11: "A nested class is a member and as such has the same access rights as any other member." and in the following example, the first comment is // OK: E::I can access E::BPerrine
This has been classified as a defect in C++03, see CWG 45, so all are "right"! (At least, the DR is about something similar, and the proposed resolution also affects your case.) g++ and clang++ have implemented the proposed (and in C++11 accepted) resolution to the defect, whereas VC++2005 has not.Perrine
@Perrine Thanks for the info - I searched for this but came up empty (My google search skills seems to have become weaker). Anyhow - it is interesting to see that compiling with g++ with -std=c++03, the above code still works - but guessing as it has been seen as a defect in the standard, it's implemented retroactively. Post you comment as an answer and I'll accept it.Percolation
@Perrine post that as an answer, I did not have a chance to loop up C++03 quote or I would have posted an answer earlier ;-)Shun
P
2

The accessibility of member of the surrounding class is affected by CWG 45. That means, at least part of the issue has been classified as a defect in the C++98 Standard. (It seems a resolution has been proposed in 2001, so I don't quite understand why it hasn't been fixed in C++03.)

In C++11, the resolution is incorporated, so the paragraph has changed to [class.access.nest]/1:

A nested class is a member and as such has the same access rights as any other member. The members of an enclosing class have no special access to members of a nested class; the usual access rules shall be obeyed. [Example:

class E {
    int x;
    class B { };

    class I {
        B b;                     // OK: E::I can access E::B
        int y;
        void f(E* p, int i) {
            p->x = i;            // OK: E::I can access E::x
        }
    };

    int g(I* p) {
        return p->y;             // error: I::y is private
    }
};

end example]

(Some) proposed resolutions to defects are implemented in the compilers; note g++ for example specifically says the -std=c++03 flag means

The 1998 ISO C++ standard plus the 2003 technical corrigendum and some additional defect reports.

So it's not obvious what you get when using this flag. However, DRs are "an indication of the intent of the committee", so they can be seen as "bugs" in the Standard, which should be fixed.


VC++2005 seems not to implement the proposed resolution, following the original Standard.

Perrine answered 15/1, 2014 at 14:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.