How to refer to protected members of an external class as default arguments for a constructor of a class template

64 Views Asked by At

Please excuse me if this is asked elsewhere (and provide a link!). I wasn't able to find an exact duplicate.

Not sure if I'm even phrasing the Q correctly.

As a minimal example:

#include <cstdint>
#include <vector>

class Priv {
protected:
    // All members of Priv are protected and should not be accessed directly.
    using Typ = uint16_t;
    //   (In reality, Typ is defined based on various macro definitions.)
    // ...
    // However, I would like to give access to classes I specifically befriend.
    // All instantiations of Public should be permitted, regardless of T.
    template <typename T> friend struct Public;
};

// Default arguments in class Public's template parameter list should be able to 
// access protected members of Priv because Public has been friend-zone'd, right?
template <typename T = Priv::Typ>
struct Public: std::vector<T> {
    inline Public(): std::vector<T>() {}
};

int main() {
        Public pub; // should default to Public<Priv::Typ>
}

But unfortunately, I seem to be declaring the friend incorrectly:

<source>:17:30: error: 'using Typ = uint16_t' is protected within this context
   17 | template <typename T = Priv::Typ>
      |  

Can someone point out my mistake?

1

There are 1 best solutions below

13
user17732522 On

The default template argument is outside the class scope, not inside it. It makes Public<> to be equivalent to Public<Priv::Typ> where clearly the argument is used in the context of the user, not the class. So it shouldn't be possible to use a protected member in this context.

You can only use the private/protected members of the class inside the friend's scope. So if you want the user of the class template to be able to specify the type either implicitly or explicitly while still having Typ marked protected, then you need to do a translation to Typ somehow inside the class scope. For example:

struct PrivTypProxy;

template <typename T = PrivTypProxy>
struct Public:
    std::vector<
        std::conditional_t<
            std::is_same_v<T, PrivTypProxy>,
            Priv::Typ,
            T
         >
      >
{
    using U = std::conditional_t<
                  std::is_same_v<T, PrivTypProxy>,
                  Priv::Typ,
                  T
               >;
    inline Public(): std::vector<U>() {}
};

But it is not clear to me what the intent for doing this is.