Accessing protected type in a class through a friend - gcc allows, clang doesn't

Viewed 110

Which compiler is right - or is this a defect in the standard?

GCC compiles the following code, but clang complains:

<source>:20:7: error: 'Impl' is a protected member of 'A'

Code:

template<typename T>
struct WrapperBuilder;

template <typename T>
struct LetMeIn : public T {
    friend struct WrapperBuilder<T>;
};

class A {
protected:
    struct Impl;
};

// without this line, gcc complains too (but I think I understand why)
extern template struct LetMeIn<A>;

template<>
struct WrapperBuilder<A> {

   A::Impl * i; // Clang doesn't like this line
};

Thank you.

https://godbolt.org/z/Qa7zZ6

Also, anyone know a workaround for clang that doesn't involve changing A?

2 Answers

clang is right. Note that gcc has many bugs with access checking in templates.

Impl is a protected member of A, WrapperBuilder<A> neither derives from A nor is it a friend of A.

LetMeIn does not bestow friendship in a relevant way. The explicit template instantiation makes WrapperBuilder<A> a friend of LetMeIn<A>, but friendship isn't transitive - that does not make it a friend of A.


LetMinIn<A>::Impl works... but very indirectly. You could do it more directly if you want:

struct LetMeIn : A {
    using Impl = A::Impl;
};

And now it's public.

Clang has it right here. When you do

template <typename T>
struct LetMeIn : public T {
    friend struct WrapperBuilder<T>;
};

WrapperBuilder<T> becomes a friend of LetMeIn<T>. That means WrapperBuilder<T> can access anything in LetMeIn<T>. Since LetMeIn<T> inherits from T it also means that WrapperBuilder<T> can access the T part of LetMeIn<T> that LetMeIn<T> has access to. It does not mean that WrapperBuilder<T> becomes a friend of T

To show this

template<>
struct WrapperBuilder<A> {

   LetMeIn<A>::Impl* foo;

};

Compiles in both clang and gcc since WrapperBuilder<A> can access LetMeIn<A> members.

Related