Passkey pattern for template class, problem with friend and template template parameter

Viewed 109

This is the classical 'passkey' pattern which allows a function to be accessible only within the scope of a specific class:

#include <iostream>

template <typename T>
class passkey {
private:
  friend T;
  passkey() {}

  // noncopyable
  passkey(const passkey&) = delete;
  passkey& operator=(const passkey&) = delete;
};

struct A {
    A();
};

void g(int i, passkey<A>) {
    std::cout << i;
}

A::A(){g(42,{});} 

int main() {

    A a;

    return 0;
}

Here the funtion g can only be called within A. But is it possible to extend it if A is now a template class? The following snippet does not compile (with clang, but it works with gcc...), because for a template friend, it seems that one must use an elaborated-class-specifier, which seems to lead to a declaration conflict...

#include <iostream>

template <template<typename...> class T>
class passkey {
private:
  template<typename...> friend class T;
  passkey() {}

  // noncopyable
  passkey(const passkey&) = delete;
  passkey& operator=(const passkey&) = delete;
};

template<typename T>
struct A {
    A();
};

void g(int i, passkey<A>) {
    std::cout << i;
}

template<typename T>
A<T>::A(){g(42,{});} 

int main() {

    A<void> a;

    return 0;
}

This gives with clang:

>source>:6:38: error: declaration of 'T' shadows template parameter
  template<typename...> friend class T;
                                     ^
<source>:3:39: note: template parameter is declared here
template <template<typename...> class T>

Is there any trick to have that work?

1 Answers

I am surprised that your variadic template actually compiles with GCC. This is how I would actually write it:

template <template<typename... Ts> class T, typename... Ts>
class passkey {
  private:
    friend class T<Ts...>;

    constexpr passkey() {
      return;
    }
    passkey(passkey const&) = delete;
    passkey& operator=(passkey const&) = delete;
};

Furthermore I would let the function g take an r-value reference and call it with std::move explicitly.

Try it here!

Related