Search code examples
c++templatesfriendinformation-hidingaccess-protection

How to hide a datum from everyone but class T


I want a type A that will yield its hidden datum to an object of type T but hide the datum from everyone else. My C++ compiler happens to be GCC 4.4, but that shouldn't matter. Why won't this work?

#include <iostream>

template <class T> class A {
  private:
    int n1;
  public:
    friend class T;
    A(const int n0 = 0) : n1(n0) {}
};

class B {
  public:
    int f(const A<B> a) const { return a.n1; }
    B() {}
};

int main() {
    const A<B> a(5);
    const B b;
    const int m = b.f(a);
    std::cout << m << "\n";
    return 0;
}

Incidentally, this works fine, except that it fails to hide the datum:

#include <iostream>

template <class T> class A {
  private:
    int n1;
  public:
    int n() const { return n1; }
    A(const int n0 = 0) : n1(n0) {}
};

class B {
  public:
    int f(const A<B> a) const { return a.n(); }
    B() {}
};

int main() {
    const A<B> a(5);
    const B b;
    const int m = b.f(a);
    std::cout << m << "\n";
    return 0;
}

Does C++ really not allow a friend class to be specified at compile time as a template parameter? Why not? If not, then what alternate technique should I use to hide the datum? (One would prefer a compile-time technique if possible.)

What is my misunderstanding here, please?

(I see some answers to related questions here and here, but either they don't answer my particular question or I fail to understand that they do so. At any rate, maybe I am using the wrong technique altogether. Though I remain interested in why the friend class T fails, what I really want to know is how to hide the datum, whether with a friend or by other means.)

Thanks.


Solution

  • I don't know the standardese behind your error (refer to Xeo's answer), but I did find a workaround for C++03. Instead of making T a friend, make one of T's member functions a friend:

    #include <iostream>
    
    template <class T> class A {
      private:
        int n1;
      public:
        friend int T::getN1(const A& a) const;
        A(const int n0 = 0) : n1(n0) {}
    };
    
    class B {
      public:
        int f(const A<B> a) const { return getN1(a); }
        B() {}
      private:
        int getN1(const A<B>& a) const {return a.n1;}
    };
    
    class C {
      public:
        int f(const A<B> a) const { return getN1(a); }
        C() {}
      private:
        // Error, n1 is a private member of A<B>
        int getN1(const A<B>& a) const {return a.n1;}
    };
    
    int main() {
        const A<B> a(5);
        const B b;
        const int m = b.f(a);
        std::cout << m << "\n";
        return 0;
    }
    

    Alternatively, you can make a nested class/struct of T be a friend of A. This may be more convenient if there are several private members of A that you want T to have access to.

    #include <iostream>
    
    template <class T> class A {
      private:
        int n1;
      public:
        friend class T::AccessToA;
        A(const int n0 = 0) : n1(n0) {}
    };
    
    class B {
      public:
        int f(const A<B> a) const { return AccessToA::getN1(a); }
        B() {};
      private:
        friend class A<B>;
        struct AccessToA
        {
            static int getN1(const A<B>& a) {return a.n1;}
        };
    };
    
    class C {
      public:
        int f(const A<B> a) const { return AccessToA::getN1(a); }
        C() {};
    
      private:
        friend class A<C>;
        struct AccessToA
        {
            // Error, n1 is a private member of A<B>
            static int getN1(const A<B>& a) {return a.n1;}
        };
    };
    
    int main() {
        const A<B> a(5);
        const B b;
        const int m = b.f(a);
        std::cout << m << "\n";
        return 0;
    }