Дружественные функции шаблона класса - PullRequest
8 голосов
/ 14 марта 2011

У меня есть шаблон класса Foo<T>.

Я хотел бы реализовать функцию, не являющуюся членом Bar, которая принимает два Foo с и возвращает Foo. Я хочу, чтобы Bar не был участником, поскольку для вызывающих абонентов будет более естественно написать Bar(f1, f2), чем f1.Bar(f2). Я также хочу, чтобы Bar было inline, потому что вычисления тривиальны и часты.

template <typename T>
inline Foo<T> Bar(const Foo<T> &lhs, const Foo<T> &rhs) {
  ...
}

Хитрость в том, что Bar нужен доступ к личным данным Foo. Я бы предпочел не иметь доступа к частным данным - нет веских оснований предоставлять личные данные пользователям. Поэтому я хотел бы сделать Bar другом Foo.

template <typename T>
class Foo {
  ...
  private:
    T w, x, y, z;
    friend Foo<T> Bar(const Foo<T> &lhs, const Foo<T> &rhs);
};

Здесь я сталкиваюсь с неприятностями. Компилятор жалуется:

Встроенный спецификатор нельзя использовать, когда объявление друга ссылается на специализацию шаблона функции.

Это правило наложено стандартом или оно специфично для MSVC ++?

Вот что я пробовал:

  • Сделать Bar постоянной публичной функцией-членом, а затем объявить версию, не являющуюся членом, которая просто возвращает lhs.Bar(rhs). Это кажется наименее хакерским решением.

  • Удалите подсказку inline, зная, что компилятор примет решение о вставке независимо от подсказки. Это тогда противоречит правилу с одним определением? Его все равно нужно будет определить в заголовочном файле, потому что это шаблон функции.

  • Объявить функцию-член с фиктивным типом шаблона:

    template <typename T>
    class Foo {
      ...
      private:
        T w, x, y, z;
    
        // Note that this declaration doesn't actually use Dummy.  It's just there to
        // satisfy the compiler.     
        template <typename Dummy>
        friend Foo<T> Bar(const Foo<T> &lhs, const Foo<T> &rhs);
    };
    

Я не совсем уверен, почему это работает, но оно удовлетворяет компилятору.

Есть ли лучшее решение?

Ответы [ 3 ]

5 голосов
/ 14 марта 2011

Если вычисление тривиально, я бы написал:

template <typename T>
class Foo {
  ...
  private:
    T w, x, y, z;
  public:
    friend Foo Bar(const Foo &lhs, const Foo &rhs) {
        ...
    }
};

Это не противоречит ODR - это встроенная функция с внешним связыванием (3.2 / 5 исключает это из ODR при условииопределения идентичны, 7.1.2 / 3 говорит, что он встроенный).

Однако это не определяет шаблон функции Bar<T>, он просто определяет набор перегрузок функций для Bar.Может быть какая-то причина, не указанная в этом вопросе, это означает, что это не сработает для вас, потому что вам действительно нужен шаблон.

2 голосов
/ 14 марта 2011

Мне больше нравится вариант 1:

template <typename T>
inline Foo<T> Bar(const Foo<T> &lhs, const Foo<T> &rhs) {
    return lhs.Bar(rhs);
}

template <typename T>
class Foo {
  ...
    Foo<T> Bar(const Foo<T> &other) const;
  private:
    T w, x, y, z;
};

Тогда функциональность безопасно содержится в классе, но для удобства вы предоставляете функцию-обертку.

1 голос
/ 14 марта 2011

Бар - это шаблон, поэтому он также должен быть шаблоном в объявлении друга.

Вам необязательно использовать фиктивный параметр, но лучше использовать

 template <typename U>
 friend Foo<U> Bar(const Foo<U> &lhs, const Foo<U> &rhs);

Вы не можете использовать T в качестве параметра шаблона здесь, так как уже есть внешний T в области видимости.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...