Точно так же конструктор, получающий параметр типа initializer_list, использует свой параметр типа T как тип элемента для своего параметра типа initializer_list:

template

Blob::Blob(std::initializer_list il):

 data(std::make_shared>(il)) { }

Подобно стандартному конструктору, этот конструктор резервирует новый вектор. В данном случае этот вектор инициализируется из параметра il.

Чтобы использовать этот конструктор, следует передать список инициализации, тип элементов которого совместим с типом элемента Blob:

Blob articles = {"a", "an", "the"};

Параметр этого конструктора имеет тип initializer_list. Каждый строковый литерал в списке неявно преобразуется в тип string.

Создание функций-членов шаблона класса

По умолчанию экземпляр функции-члена шаблона класса создается, только если программа использует эту функцию-член. Рассмотрим следующий код:

// создает экземпляр Blob и конструктор initializer_list

Blob squares = {0,1,2,3,4,5,6,7,8,9};

// создает экземпляр Blob::size() const

for (size_t i = 0; i != squares.size(); ++i)

 squares[i] = i*i; // создает экземпляр Blob::operator[](size_t)

Этот код создает экземпляр класса Blob и трех его функций-членов: operator[](), size() и конструктора initializer_list().

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

По умолчанию экземпляр члена шаблона класса создается, только если он используется.

Упрощение использования имени шаблона класса в коде класса

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

// BlobPtr передает исключение при попытке доступа к несуществующему

// элементу

template class BlobPtr

public:

 BlobPtr(): curr(0) { }

 BlobPtr(Blob &a, size_t sz = 0):

  wptr(a.data), curr(sz) { } T& operator*() const {

  auto p = check{curr, "dereference past end");

  return (*p)[curr]; // (*p) - вектор, на который указывает этот

                     // объект

 }

 // инкремент и декремент

 BlobPtr& operator++(); // префиксные операторы

 BlobPtr& operator--();

private:

 // если проверка успешна, check() возвращает shared_ptr на вектор

 std::shared_ptr>

  check(std::size_t, const std::string&) const;

 // хранит weak_ptr, а значит, базовый вектор может быть удален

 std::weak_ptr> wptr;

 std::size_t curr; // текущая позиция в пределах массива

};

Внимательные читатели, вероятно, обратили внимание на то, что префиксные функции-члены инкремента и декремента шаблона класса BlobPtr возвращают тип BlobPtr&, а не BlobPtr&. В области видимости шаблона класса компилятор рассматривает ссылки на сам шаблон так, как будто были подставлены аргументы шаблона, соответствующие собственным параметрам. Таким образом, этот код эквивалентен следующему:

BlobPtr& operator++();

BlobPtr& operator--();

Использование имени шаблона класса вне тела шаблона

При определении функций-членов вне тела шаблона класса следует помнить, что код находится не в области видимости класса, пока не встретилось имя класса (см. раздел 7.4):

// постфикс: осуществляет инкремент/декремент объекта, но возвращает

// неизменное значение

template

BlobPtr BlobPtr::operator++(int) {

 // никакой проверки здесь не нужно; ее выполнит вызов префиксного

 // инкремента

 BlobPtr ret = *this; // сохранить текущее значение

 ++*this;             // перемещение на один элемент; префиксный ++

                      // проверяет инкремент

Перейти на страницу:

Похожие книги