Как разделить определение функции друга шаблона внутри класса шаблона? - PullRequest
4 голосов
/ 12 мая 2010

Следующий пример компилируется нормально, но я не могу понять, как разделить объявление и определение оператора << () в этом конкретном случае. </p>

Каждый раз, когда я пытаюсь разделить определение, у друга возникают проблемы, и gcc жалуется, что определение оператора << () должно принимать ровно один аргумент. </p>

#include <iostream>
template <typename T>
class Test {
    public:
        Test(const T& value) : value_(value) {}

        template <typename STREAM>
        friend STREAM& operator<<(STREAM& os, const Test<T>& rhs) {
            os << rhs.value_;
            return os;
        }
    private:
        T value_;
};

int main() {
    std::cout << Test<int>(5) << std::endl;
}

Оператор << () должен иметь свободный первый параметр для работы с различными видами выходных потоков (std :: cout, std :: wcout или boost :: asio :: ip :: tcp :: iostream). Второй параметр должен быть привязан к специализированной версии окружающего класса. </p>

Test<int> x;
some_other_class y;

std::cout << x; // works
boost::asio::ip::tcp::iostream << x; // works

std::cout << y; // doesn't work
boost::asio::ip::tcp::iostream << y; // works

Кроме того, использование функции, не являющейся членом, не эквивалентно разделению определения и объявления, поскольку функции, не являющиеся членами, не могут получить доступ к закрытым атрибутам класса.

Ответы [ 5 ]

4 голосов
/ 12 мая 2010

Возможно, проще всего подружиться со всеми этими операторами шаблонов:

#include <iostream>
template <typename T>
class Test
{
    public:
        Test(const T& value) : value_(value) {}

        template <typename STREAM, typename U>
        friend STREAM& operator<<(STREAM& os, const Test<U>& rhs);

    private:
        T value_;
};

template <typename STREAM, typename T>
STREAM& operator<<( STREAM& os, const Test<T>& rhs )
{
    os << rhs.value_;
    return os;
}
1 голос
/ 12 мая 2010

Ближайшее, чего я могу достичь, это

#include <iostream>

template <typename T>
class Test;

template <typename STREAM, typename T>
STREAM& operator<<(STREAM& os, const Test<T>& rhs);

template <typename T>
class Test {
public:
    Test(const T& value) : value_(value) {}

    template <typename STREAM, typename U>
    friend STREAM& operator<< (STREAM& os, const Test<U>& rhs);

private:
    T value_;
};

template <typename STREAM, typename T>
STREAM& operator<<(STREAM& os, const Test<T>& rhs) {
    os << rhs.value_;
    return os;
}

int main() {
    std::cout << Test<int>(5) << std::endl;
}

, который объявляет весь оператор << как друга, а не только один, параметризованный T. Проблема в том, что невозможно частично специализировать функции. Один хотел бы использовать </p>

template <typename STREAM>
friend STREAM& operator<< <STREAM, T> (STREAM& os, const Test<T>& rhs);

но это неверный синтаксис. (Ну и частичная специализация не может быть объявлена ​​другом)

1 голос
/ 12 мая 2010

Разве это не должно быть определено вне класса?

template <typename T>
class Test 
{  
    ...
    template <typename STREAM>
    friend STREAM& operator<<(STREAM& os, const Test<T>& rhs);
};

template <typename STREAM, typename T> 
STREAM& operator<<(STREAM& os, const Test<T>& rhs) 
{
    os << rhs.value_;
    return os;
}
0 голосов
/ 12 мая 2010

Для каждого экземпляра типа T класса Test предоставляется оператор функции шаблона << (), который может работать с потоками различного типа. Функция operator << () имеет свободный первый параметр, но фиксированный второй параметр. </p>

пример:

Test<int> x;
some_other_class y;

std::cout << x; // works
boost::asio::ip::tcp::iostream << x; // works

std::cout << y; // doesn't work
boost::asio::ip::tcp::iostream << y; // works

Так должен был работать тестовый класс.

0 голосов
/ 12 мая 2010

Проблема в том, что в коде, который вы представляете другу, есть шаблонная функция, параметризованная только для первого типа аргумента. То есть для каждого экземпляра типа T шаблона класса (назовите его mytype) вы объявляете бесплатную функцию шаблона:

template <typename STREAM>
STREAM& operator<<( STREAM& os, Test<mytype> const & x );

Важным моментом здесь является то, что Test<mytype> - это конкретный экземпляр Test с аргументом типа mytype.

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

С другой стороны, я рекомендую не параметризировать operator<< для типа потока и в то же время определять его внутри фигурных скобок, поскольку он имеет небольшие преимущества (правила поиска имен немного отличаются) .

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