Дополнительные аргументы шаблона для объявления друга в классе шаблона? - PullRequest
0 голосов
/ 29 сентября 2019

Чтобы какая-то глобальная функция

template<typename T, int count>
void func (const Obj<T>& obj) {
    for (int i = 0; i < count; i++)
        std::cout << obj.value << std::endl;
}

могла получить доступ к закрытому полю value некоторого шаблонного класса

template<typename T>
class Obj {
    public:
        Obj (T value);
    private:
        T value;
};

template<typename T>
Obj<T>::Obj (T value) : value(value) {}

, нам нужно объявить func<T, count>друг Obj<T>.Но func<T, count> должен быть объявлен прежде, чем мы сможем сделать его другом Obj<T>, и для этого нам нужно заранее объявить Obj<T>.Результирующий код выглядит следующим образом:

// Forward declarations
template<typename T>
class Obj;

template<typename T, int count>
void func (const Obj<T>& obj);

// Obj<T>
template<typename T>
class Obj {
    public:
        Obj (T value);

        template<int count>
        friend void func<T, count> (const Obj<T>& obj);
    private:
        T value;
};

template<typename T>
Obj<T>::Obj (T value) : value(value) {} // <-- ERROR

// func<T>
template<typename T, int count>
void func (const Obj<T>& obj) {
    for (int i = 0; i < count; i++)
        std::cout << obj.value << std::endl;
}

Но это заставляет gcc жаловаться на «недопустимое использование идентификатора шаблона 'func' в объявлении основного шаблона», так как же я на самом деле объявляю func<T, count> другом?Obj<T> для каждого count?Согласно этому ответу Мне просто нужно заменить объявление друга на

template<typename T1, int count>
friend void func (const Obj<T1>& obj);

Насколько я знаю, это сделает func<T1, count> другом Obj<T> независимо от того, T1 и T совпадают, что абсурдно.Можно ли объявить func<T, count> другом Obj<T> и никаким другим Obj<T1>?(желательно таким образом, чтобы определение func<T, count> не входило в определение Obj<T>)

(я знаю, что мог бы просто сделать count реальным параметром, но приведенный выше пример является просто упрощениеммой настоящий код. В действительности я пытаюсь перегрузить std::basic_ostream<CharT, Traits>& operator<< (std::basic_ostream<CharT, Traits>& stream, const Obj<T>& obj) для некоторого класса Obj<T> таким образом, чтобы operator<< мог получить доступ к приватным полям Obj<T>.)

1 Ответ

1 голос
/ 29 сентября 2019

Объявление friend должно соответствовать любому возможному предварительному объявлению и, конечно, определению, включая аргументы шаблона.

Это означает, что вам нужно, например,

template<typename U, int count>
friend void func(const Obj<U>& obj);

Это не имеет значенияесли аргумент шаблона класса T и аргумент шаблона функции U различны, так как в любом случае будут выполняться вызовы правильной функции.

Пример:

Obj<int> int_obj;
Obj<float> float_obj;

func<X>(int_obj);  // Will call void func<int, X>(int_obj)
func<X>(float_obj);  // Will call void func<float, X>(float_obj)

В качестве альтернативы вы можете определить функцию, встроенную в определение класса, и тогда вам не нужно предоставлять T или U аргументы шаблона:

template<int count>
friend void func(const Obj<T>& obj)
{
    // Implementation...
}

И ни в том, ни в другом случае у вас не должно быть предварительного заявления func (как упомянуто в моем комментарии).

...