2010-05-12 3 views
3

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

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

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

< < Оператор() должен иметь свободный первый параметр для работы с различными видами выходных потоков (станд :: COUT, станд :: wcout или повышение :: ASIO :: IP :: ТСР: : iostream). Второй параметр должен быть привязан к специализированной версии окружающего класса.

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

Возможный дубликат: http://stackoverflow.com/questions/476272/how-to-properly-overload-the-operator-for-an-ostream – Stephen

+0

Почему вы хотите параметризовать тип OSTREAM? Общая идиома будет определять оператор << как: 'friend std :: ostream & operator << (std :: ostream & os, Test & rhs) {...}' –

+0

@dribeas: потому что я хочу иметь возможность использовать другие выходных потоков. Как boost :: asio :: ip :: tcp :: iostream и boost :: asio :: local :: stream_protocol :: iostream. – joke

ответ

4

Самые простое, вероятно, чтобы сделать все эти операторы шаблона друзей:

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

, указав второй параметр шаблона для оператора <<(). – joke

+0

Я не мог найти способ повторно использовать 'T', даже типичную функцию члена шаблона, содержащую 2 объявления' template', предшествующие определению функции. Это не имеет значения с точки зрения доступности, но, тем не менее, кажется странным. –

1

Не следует ли определять его за пределами класса?

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

Вы пытались скомпилировать это? –

+0

обычно можно было бы написать: шаблон шаблон STREAM & Test :: оператор << (STREAM & OS, Const Test & РИТ) { Os << rhs.value_; return os; }; Но в этом случае это не сработает. – joke

+0

Даже если это сработает, в примере все еще не хватает: Тест :: оператор << (...) – joke

1

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

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

который объявляет весь оператор < <, как друг, а не только тот, который параметризирован Т. Задача состоит в том, что невозможно частично специализировать функции. Хотелось бы использовать

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

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

+0

Вы правы, это не то же самое, что оригинал. Но поскольку исходная версия может быть определена в определении класса, должен быть способ разделения объявления и определения. – joke

+0

Оригинал определяет функцию с одним параметром шаблона, первым. Если вы хотите что-то эквивалентное, вам нужно будет определить одну функцию для типа T. Это возможно, но, вероятно, больно. – AProgrammer

+0

Да, он объявляет/определяет функцию члена-шаблона с одним параметром шаблона, как только экземпляр класса окружения. – joke

0

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

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

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

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

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

+0

@dribeas: http://stackoverflow.com/questions/2819994/2820743#2820743 – joke

0

Для каждого экземпляра типа T класса проверяется оператор функции шаблона < <(), который может работать на разных типах потоков. Оператор < <() имеет свободный первый параметр, но фиксированный второй параметр.

пример:

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 

Вот так класс Test должен был работать.

+0

Не 'boost :: asio :: ip :: tcp :: iostream' и' boost :: asio :: ip :: tcp :: iostream' наследует от 'std :: basic_iostream', который сам наследует от' std :: basic_ostream' ?? –

+0

Я не думаю, что они наследуют. Они должны быть типовыми специализациями. boost :: asio :: ip :: tcp :: iostream должен быть специализацией шаблона basic_socket_iostream. – joke

+0

Просто следуйте коду ... (boost/asio/basic_socket_iostream.hpp: 'template <...> class basic_socket_iostream: public boost :: base_from_member <...>, public std :: basic_iostream ').Это просто имеет смысл, все дело в том, что вам не нужно переписывать любой «оператор <<» для использования asio iostreams. Если вы можете написать 'cout', вы можете написать поток tcp. –

Смежные вопросы