I don't understand why in the following code, I am allowed to create the function print_private_template
while the compiler complains about print_private_class
:
#include <cstdio>
class A
{
private:
template <unsigned T>
struct B
{
};
struct C
{
};
public:
template <unsigned T>
B<T> getAb()
{
return B<T>();
}
C getAc()
{
return C();
}
};
template<unsigned T>
void print_private_template(const A::B<T> &ab)
{
printf("%d\n", T);
}
void print_private_class(const A::C &ac)
{
printf("something\n");
}
int main(int, char**)
{
A a;
print_private_template(a.getAb<42>());
print_private_class(a.getAc());
return 0;
}
Is this an expected behaviour? a compiler bug/extension?
Just to be clear, my goal is to make the compiler error on both the usage of print_private_template
and print_private_class
.
Comeau does give an error (when you comment out the print_private_class
function and its call in strict C++03 mode.
ComeauTest.c(31): error: class template "A::B" (declared at line 7) is inaccessible
void print_private_template(const A::B &ab)
^
detected during instantiation of "print_private_template" based on
template argument <42U> at line 45
G++ 4.5 on Windows does not report any error with -std=c++ -Wall -pedantic
though.
Your class A::C
and class template A::B<T>
both have the same visibility as any other normal members. Hence, both print_private_class
and print_private_template
require a diagnostic.
11.8 Nested classes [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 (Clause 11)
shall be obeyed.
As stated by Dirk Gently, GCC doesn't perform access control when instantiating template structs / classes nested in other (template) structs / classes.
One way to work around this is to encapsulate them in a non-template struct:
template<int I> class MyTemplate
{
struct PT
{
template<int, typename = void> struct InnerTemplate;
// ... specialisations here ...
};
public:
typedef typename PT::template InnerTemplate<I>::SomeType SomeType;
};
typedef MyTemplate<1>::PT::InnerTemplate<1> ThisWontWork;
The last line will fail to compile with the error:
error: 'struct MyTemplate<1>::PT' is private within this context
I'll grant that this is ugly, especially having to use PT::template
but it seems to effectively prevent clients from instantiating helper templates they aren't meant to access, so it's worth a shot.