Я пытаюсь улучшить свое понимание шаблонов. В качестве теста я хотел бы перегрузить operator<<
потока operator<<
чтобы он работал на шаблоном классе вместе с (templated) basic_ostream
из STL. Вот минимальный пример:
#include <iostream>
using namespace std;
//Forward declarations
template< typename T >
class Container;
template< typename T, typename charT, typename traits >
basic_ostream< charT, traits >&
operator<<( basic_ostream< charT, traits >& out,
const Container< T >& a_container );
//Definitions
template< typename T >
class Container
{
public:
Container( T a_value ): value( a_value ){}
private:
T value;
template< typename charT, typename traits >
friend basic_ostream< charT, traits >&
operator<<( basic_ostream< charT, traits >& out,
const Container< T >& a_container );
};
template< typename T, typename charT, typename traits >
basic_ostream< charT, traits >&
operator<<( basic_ostream< charT, traits >& out,
const Container< T >& a_container )
{
out << a_container.value;
return out;
}
//Main
int main( void )
{
Container< int > my_container( 42 );
cout << my_container;
return 0;
}
Однако, когда я пытаюсь скомпилировать этот код, компоновщик не может найти перегруженную функцию:
/home/Ae6PGM/cc5xj2iM.o: In function 'main':
prog.cpp:(.text.startup+0x21): undefined reference to 'std::basic_ostream<char,
std::char_traits<char> >& operator<< <char, std::char_traits<char> >
(std::basic_ostream<char, std::char_traits<char> >&, Container<int> const&)'
collect2: error: ld returned 1 exit status
Я могу исправить эту ошибку, если я делаю каждый экземпляр экземпляра моего перегруженного operator<<
другом, но это кажется более правильным, если только экземпляры, соответствующие типу контейнера, являются друзьями. Вот ссылка на рабочий, но неверный вариант: http://ideone.com/DNQzlB.
Другой альтернативой является определение функции внутри объявления класса, но я хотел бы избежать этого, если это возможно.
Существует аналогичный, но чуть более простой вопрос здесь. Существует большой ответ, но я не могу понять, как его адаптировать к моей проблеме.
Благодаря,
Андрей
Исходное объявление вашего шаблона взяло три параметра шаблона { T, charT, traits }
но в объявлении friend
только 2 { charT, traits }
. Вы получаете неопределенную ссылку, потому что компилятор рассматривает их как две отдельные функции, одна из которых не определена.
Вам нужно дать ему третий аргумент шаблона:
template< typename T2, typename charT, typename traits > friend basic_ostream< charT, traits >& operator<< ( basic_ostream< charT, traits >& out, const Container< T2 >& a_container );
T2
функции не соответствует типу T
контейнера. Это разумный обходной путь на практике, но я хотел бы понять, есть ли более правильный способ сделать это.
Спасибо, что задали этот вопрос. Я узнал совсем немного, просмотрев его.
Тем не менее, я думаю, я понимаю, что вы пытаетесь спросить. Если я правильно вас понимаю, вы пытаетесь внедрить и контролировать неявное создание экземпляра с помощью шаблона функции.
Хотя приведенный здесь пример перегрузки operator<<
, механизм, лежащий в основе этого общего программирования с использованием шаблона, одинаковый для реализации шаблона функции.
Когда вы указываете cout << my_container
, то, что вы создаете, является перегруженным operator<<
. Но если вы хотите создать экземпляр объекта без шаблона, вам придется изменить то, что вы пытаетесь прочитать.
Поскольку значение элемента данных класса является закрытым, вы можете использовать указатель, чтобы явно указать на этот элемент данных, и использовать этот указатель для создания экземпляра объекта без шаблона. Взгляните на следующий пример: int* private_value = (int*)&my_container;
*private_value
должно позволить вам получить доступ к частному члену, несмотря на инкапсуляцию.
Если вы хотите повысить безопасность членов данных, то для предотвращения такой реализации необходимо более глубокое инкапсулирование.
Вывод аргумента шаблона может быть достигнут (если это то, что вы ищете), возможно, включая функцию get()
и доступ к value
, тем самым создавая экземпляр объекта без шаблона.