Как скрыть данные от всех, кроме класса T - PullRequest
5 голосов
/ 18 марта 2012

Мне нужен тип A , который выдаст свои скрытые данные объекту типа T , но скрыт данные от всех остальных.Мой компилятор C ++ это GCC 4.4, но это не должно иметь значения.Почему это не сработает?

#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;
}

Кстати, это работает нормально, за исключением того, что не удается скрыть данные:

#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;
}

Разве C ++ действительно не позволяет классу друга бытьуказан во время компиляции в качестве параметра шаблона?Почему бы и нет?Если нет, то какую альтернативную технику я должен использовать, чтобы скрыть данные?(Можно было бы предпочесть метод компиляции, если это возможно.)

В чем мое недоразумение, пожалуйста?

(я вижу некоторые ответы на смежные вопросы здесь и здесь, но либо они не отвечают на мой конкретный вопрос, либо я не понимаю, что они так делают. Во всяком случае, возможно, я вообще использую неправильную технику. Хотя меня по-прежнему интересует, почему Класс друга T терпит неудачу, я действительно хочу узнать, как скрыть данные, будь то с другом или другими способами.)

Спасибо.

Ответы [ 2 ]

4 голосов
/ 18 марта 2012

Ваш компилятор просто слишком стар. C ++ 11 позволяет вам объявить параметры шаблона друзьями.

§11.3 [class.friend] p3

Объявление друга, которое не объявляет функцию, должно иметь одну из следующих форм:

  • friend elaborated-type-specifier ;
  • friend simple-type-specifier ;
  • friend typename-specifier ;

Если спецификатор типа в объявлении friend обозначает тип класса (возможно, cv-квалифицированный), этот класс объявляется как friend; в противном случае объявление друга игнорируется.

И даже содержит пример параметра шаблона в качестве друга:

class C;
// [...]
template <typename T> class R {
  friend T;
};

R<C> rc;   // class C is a friend of R<C>
R<int> ri; // OK: "friend int;" is ignored

C ++ 03, к сожалению, не имеет возможности сделать это, однако вы можете просто добавить в друзья одну свободную функцию и позволить ей действовать как «склеивающий» код, который берет данные из одного класса и передает их другому. Другим способом может быть шаблон ключа доступа .

1 голос
/ 18 марта 2012

Я не знаю стандартную причину вашей ошибки (см. Ответ Xeo), но я нашел обходной путь для C ++ 03. Вместо того, чтобы T сделать другом, сделайте одну из функций-членов T другом:

#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;
}

В качестве альтернативы, вы можете сделать вложенный класс / структуру T другом для A. Это может быть более удобно, если есть несколько частных членов A, к которым вы хотите, чтобы T имел доступ.

#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;
}
...