- •Ооп: Лекция 6. Шаблоны. Основы обобщенного программирования.
- •Введение в шаблоны - шаблоны функций
- •Автоматический вывод типов в шаблонах
- •Шаблоны классов
- •Технические проблемы использования шаблонов
- •Константы в аргументах шаблона
- •Компоновка шаблонов
- •Пример: собственная шаблонная реализация std::initializer_list
- •Пример: шаблонная реализация связного списка
- •Обобщенные алгоритмы
- •Полные примеры из лекции
Пример: собственная шаблонная реализация std::initializer_list
Полученной информации о шаблонах и принципах обобщенного программирования вполне достаточно для достижения более глубокого понимания происходящего в элементах стандартной библиотеки, где определения-шаблоны встречаются очень часто. В частности, рассмотрим один из возможных способов реализации списков инициализаторов, наподобие std::initializer_list, с применением шаблона.
Ниже представлено объявление и реализация шаблона класса такого списка, по составу очень близкая к стандартному средству std::initializer_list. Объект такого класса позволяет удовлетворять минимальным требованиям интервального цикла for.
initializer_list.hpp
#ifndef _INITIALIZER_LIST_HPP_
#define _INITIALIZER_LIST_HPP_
/*****************************************************************************/
template< typename T >
class InitializerList
{
/*-----------------------------------------------------------------*/
public:
/*-----------------------------------------------------------------*/
// Конструктор по умолчанию
InitializerList ();
// Конструктор, принимающий интервал указателей на данные массива
InitializerList ( const T * _pBegin, const T * _pEnd );
/*-----------------------------------------------------------------*/
// Метод доступа к указателю на начало массива
const T * begin () const;
// Метод доступа к указателю на элемент массива, следующий за последним
const T * end () const;
// Метод подсчета размера массива
size_t size () const;
/*-----------------------------------------------------------------*/
// Вспомогательный синоним типа для хранимого элемента данных
typedef T value_type;
/*-----------------------------------------------------------------*/
private:
/*-----------------------------------------------------------------*/
// Указатель на начало массива
const T * m_pBegin;
// Указатель на элемент массива, следующий за последним
const T * m_pEnd;
/*-----------------------------------------------------------------*/
};
/*****************************************************************************/
// Конструктор по умолчанию
template< typename T >
InitializerList< T >::InitializerList ()
: m_pBegin( nullptr )
, m_pEnd( nullptr )
{}
/*****************************************************************************/
// Конструктор, принимающий интервал указателей на данные массива
template< typename T >
InitializerList< T >::InitializerList ( const T * _pBegin, const T * _pEnd )
: m_pBegin( _pBegin )
, m_pEnd( _pEnd )
{}
/*****************************************************************************/
// Метод доступа к указателю на начало массива
template< typename T >
const T * InitializerList< T >::begin () const
{
return m_pBegin;
}
/*****************************************************************************/
// Метод доступа к указателю на элемент массива, следующий за последним
template< typename T >
const T * InitializerList< T >::end () const
{
return m_pEnd;
}
/*****************************************************************************/
// Метод подсчета размера массива
template< typename T >
size_t InitializerList< T >::size () const
{
return m_pEnd - m_pBegin;
}
/*****************************************************************************/
#endif // _INITIALIZER_LIST_HPP_
Стоит обратить внимание на синоним типа value_type, объявленный в открытой части данного класса. Наличие подобных синонимов типично для стандартной библиотеки, и делает возможным реализацию ряда алгоритмов, которым необходимо ссылаться на тип данных хранимого в массиве элемента для решения задачи.
Ниже приведена тестовая программа, где созданный шаблон класса инстанцируется трижды с различными типами данных (int, double, std::string). Содержимое списков перебирается интервальным циклом for и последовательно выводится на экран, при этом, для всех типов списков используется один и тот же вспомогательный шаблон глобальной функции printList.
test_initializer_list.cpp
/*****************************************************************************/
#include "initializer_list.hpp"
#include <string>
#include <iostream>
/*****************************************************************************/
// Шаблон вспомогательного метода для вывода списка на экран
template< typename L >
void printList ( L _l )
{
// Выводим количество элементов
std::cout << "List(" << _l.size() << "): ";
// Перебираем все элементы и выводим их на экран последовательно
for ( typename L::value_type y : _l ) // используем вложенный синоним value_type
std::cout << y << ' ';
std::cout << std::endl;
}
/*****************************************************************************/
int main ()
{
// Массив целых чисел
int data1[] = { 1, 2, 3, 4, 5 };
int size1 = sizeof ( data1 ) / sizeof( int );
// Массив действительных чисел
double data2[] = { 1.0, 2.0, 3.0, 4.0 };
int size2 = sizeof( data2 ) / sizeof( double );
// Массив строк
std::string data3[] = { "A", "B", "C" };
int size3 = sizeof( data3 ) / sizeof( std::string );
// Формируем по объекту InitializerList для каждого из массивов
InitializerList< int > list1( data1, data1 + size1 );
InitializerList< double > list2( data2, data2 + size2 );
InitializerList< std::string > list3( data3, data3 + size3 );
// Выводим все три списка на экран
printList( list1 );
printList( list2 );
printList( list3 );
}
/*****************************************************************************/
Единственным существенным недостатком по сравнению со стандартным элементом, очевидно, является отсутствие поддержки неявного конструирования при использовании агрегатных литералов для временных массивов (выражения в стиле {1,2,3} без объявления массива явно). Вмешаться в данную логику компилятора по выбору стандартной реализации списка инициализаторов не представляется возможным.
