I use the (simplified) following code to implement pimpl idiom
// in .h
#include <memory>
template<class T, class Impl = typename T::Impl> class Pimpl {
public:
Pimpl() : m_impl(std::make_unique<Impl>()) {}
Impl * operator->() { return m_impl.get(); }
const std::unique_ptr<Impl> m_impl;
};
class MyClass {
public:
int getData();
private:
class Impl;
Pimpl<MyClass> m_impl;
};
// in .cpp
class MyClass::Impl {
public:
int data = 1;
};
int MyClass::getData() {
return m_impl->data;
}
int main() {
MyClass c;
return c.getData();
}
It works fine with g++, but building with clang++ cause an error
error: 'Impl' is a private member of 'MyClass'
template<class T, class Impl = typename T::Impl> class Pimpl
^
This may seems reasonable, but if I replace Pimpl<MyClass> m_impl;
by Pimpl<MyClass, Impl> m_impl;
in MyClass then clang++ build without issue and hapily create an instance of the "private" class inside Pimpl::Pimpl().
So is using a private inner class as template parameter default value a valid behaviour (and there is an issue in clang) ? Or is it forbidden (and gcc is too permissive) ? Or is is not specified ?
This question How can I use a private inner template class as a parameter in a template specialization? is similar but context (template specialization) and answer ("the template is never actually instantiated") does not seem relevant here.
Tested with clang 10.0.0 and gcc 9.4.0. Code uses c++17.
I believe clang conforms to the standard here. According to [temp.arg]/3,
The name of a template-argument shall be accessible at the point where it is used as a template-argument. ... For a template-argument that is a class type or a class template, the template definition has no special access rights to the members of the template-argument.
The implicit instantiation of Pimpl<MyClass, MyClass::Impl>
inside MyClass
is invalid actually.