Вход/Регистрация
Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ
вернуться

Майерс Скотт

Шрифт:

Однако не всегда в C++ ключевые слова class и typename эквивалентны. Иногда вы обязаны использовать typename. Чтобы понять – когда именно, поговорим о двух типах имен, на которые можно ссылаться в шаблоне.

Предположим, что у нас есть шаблон функции, принимающей в качестве параметра совместимый с STL-контейнер, содержащий объекты, которые могут быть присвоены величинам типа int. Далее предположим, что эта функция просто печатает значение второго элемента. Это не очень содержательная функция, которая к тому же и реализована по-дурацки. Как я уже говорил, она даже не будет компилироваться, но забудьте об этом на время – все это не так глупо, как кажется:

template <typename C> // печатает второй

void print2nd(const C& container) // элемент контейнера

{ // это некорректный C++!

if (container.size >= 2) {

C::const_iterator iter(container.begin); // получить итератор,

// указывающий на первый

// элемент

++iter; // сместиться на второй

// элемент

int value = *iter; // скопировать элемент в int

std::cout << value; // напечатать int

}

}

Я выделил в этой функции две локальные переменные – iter и value. Типом iter является C::const_iterator – он зависит от параметра шаблона C. Имена в шаблоне, которые зависят от параметра шаблона, называются зависимыми именами. Зависимое имя внутри класса я буду называть вложенным зависимым именем. C::const_iterator – это вложенное зависимое имя. Фактически это даже вложенное зависимое имя типа, то есть вложенное имя, которое относится к типу.

Другая локальная переменная в print2nd – value – имеет тип int, а int – это имя, которое не зависит ни от какого параметра шаблона. Такие имена называются независимыми.

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

template <typename C> // печатает второй элемент контейнера

void print2nd(const C& container) // это некорректный C++!

{

C::const_iterator *x;

...

}

Выглядит так, будто мы объявили x как локальную переменную – указатель на C::const_iterator. Но это только видимость, поскольку мы «знаем», что C::const_iterator является типом. А что, если в классе C есть статический член данных по имени const_iterator и что, если x будет именем глобальной переменной? В этом случае приведенный код не будет объявлять локальную переменную, а окажется умножением C::const_iterator на x! Звучит невероятно, но это возможно, и авторы синтаксических анализаторов исходного кода на C++ должны позаботиться обо всех возможных вариантах входных данных, даже самых сумасшедших.

Пока о C ничего не известно, мы не можем узнать, является ли C::const_iterator типом или нет, а во время разбора шаблона print2nd компилятор ничего о C не знает. В C++ предусмотрено правило, разрешающее эту неопределенность: если синтаксический анализатор встречает вложенное зависимое имя в шаблоне, он предполагает, что это не имя типа, если только вы не укажете это явно. По умолчанию вложенные зависимые имена не являются типами. Есть исключение из этого правила, о котором я расскажу чуть ниже.

Имея это в виду, посмотрите опять на начало print2nd:

template <typename C>

void print2nd(const C& container)

{

if (container.size >= 2) {

C::const_iterator iter(container.begin); // предполагается, что

... // это не имя типа

Теперь должно быть ясно, почему это некорректный C++. Объявление iter имеет смысл только в случае, если C::const_iterator является типом, но мы не сообщили C++ об этом, потому C++ предполагает, что это не так. Чтобы исправить ситуацию, мы должны сообщить C++, что C::const_iterator – это тип. Для этого мы помещаем ключевое слово typename непосредственно перед ним:

template <typename C> // это корректный С++

void print2nd(const C& container)

{

if (container.size >= 2) {

typename C::const_iterator iter(container.begin);

...

}

}

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

  • Читать дальше
  • 1
  • ...
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • ...

Ебукер (ebooker) – онлайн-библиотека на русском языке. Книги доступны онлайн, без утомительной регистрации. Огромный выбор и удобный дизайн, позволяющий читать без проблем. Добавляйте сайт в закладки! Все произведения загружаются пользователями: если считаете, что ваши авторские права нарушены – используйте форму обратной связи.

Полезные ссылки

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

Подпишитесь на рассылку: