Use the using directive on a private nested class

267 views Asked by At

I have been developing a library for a while and now that I think it's ready, I am trying to follow the pimpl principle in order to hide implementation details.

// File Class.h
class Class {
public:
  Class();

private:
    class ClassImpl;
    ClassImpl* rep;
};

// File Class.cpp
#include "Class.h"
#include "ClassImpl.h"
Class::Class() {
    rep = new ClassImpl();
}

The implementation class is defined in another file as follow

// File ClassImpl.h
#include "Class.h"
class Class::ClassImpl {
public:
  ClassImpl();
};

And its implementation:

// File ClassImpl.cpp
#include "ClassImpl.h"
#include <iostream>

using C = Class::ClassImpl; // error: 'class Class::ClassImpl' is private
C::ClassImpl() {
  std::cout << "Implementation constructor";
}

Main function:

// File main.cpp
#include "Class.h"
int main() {
    Class c;
    return 0;
}

Doing this, the compiler says error: 'class Class::ClassImpl' is private on the using line in ClassImpl.cpp. If I remove it and use Class::ClassImpl instead, everything works fine.

Is there a way to use usingon the private nested class ClassImpl ?

2

There are 2 answers

1
YSC On

Is there a way to use usingon the private nested class ClassImpl ?

Yes. Declare ClassImpl with public access in Class:

class Class {
public:
    Class();
    class ClassImpl;

private:
    ClassImpl* rep; // should be reference, see note.
};

// ...

using C = Class::ClassImpl;

Live demo

You can also make ClassImpl not a nested class.

Note: you might prefer having Class::rep to be a reference rather than a (naked) pointer. There is no reason for a valid Class instance not to have an implementation object (a null pointer).

0
Peter On

Is there a way to use using on the private nested class ClassImpl?

The short answer is "no".

Longer answer follows.

What you're trying to do is a using declaration, not a using directive.

Your using declaration

using C = Class::ClassImpl;

is at file scope, so the name Class::ClassImpl cannot be used directly as a name in that scope.

The first relevant section of the standard (C++17) is Section 10.3.3 "The using declaration", for which para 19 states "A synonym created by a using-declaration has the usual accessibility for a member-declaration".

To find what is meant by "usual accessibility", refer to Section 14 "Member Access Control", which states that "private names can only be used by members and friends of the class in which it is declared". The usage of "names" here is quite specific - anything with a name that is private to a class (member declarations, type declarations, etc) are treated the same way.

If you want to have a using declaration at file scope, as per your example;

using C = Class::ClassImpl;

then ClassImpl needs to be a public name (of a nested class) of Class. private and protected names of a class cannot be accessed at file scope.