LiraNuna
LiraNuna

Reputation: 67261

Why does GCC allow private nested template classes/structs to be visible from global template functions?

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.

Upvotes: 4

Views: 2047

Answers (3)

It got fixed for GCC 11

Ten years later... and the bug got fixed for GCC 11: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=41437#c13 (another point in the dupe pool had been previously linked to by dirkgently).

A minimal reproduction:

main.cpp

class Out {
  protected:
    class In {};
};

template <class C>
void f() { Out::In in; }

int main() {
    f<Out>();
}

still compiles in GCC 10.2 with all warnings enabled:

g++-10 -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o main.out main.cpp

but fails correctly in clang++ 10:

void f() { Out::In in; }
                ^
main.cpp:3:11: note: declared protected here
    class In {};
          ^
1 error generated

The above fails to fail in GCC because f is a template function.

Upvotes: 1

Anonymous
Anonymous

Reputation: 21

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.

Upvotes: 1

dirkgently
dirkgently

Reputation: 111150

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.

Upvotes: 2

Related Questions