Программирование >>  Обобщенные обратные вызовы 

1 ... 11 12 13 [ 14 ] 15 16 17 ... 84


3. Иначе, если имя друга квалифицировано с использованием имени класса или пространства имен (например Some: : Name) И этот класс или пространство имен содержит подходящий шаблон функции (с выводимыми аргументами шаблона)

то другом является специализация этого шаблона функции.

4. В противном случае имя должно быть неквалифицированным и объявляет (возможно, повторно) обычную (нешаблонную) функцию.

Понятно, что второй и четвертый пункты соответствуют нешаблонным сущностям, так что для объявления специализации шаблона в качестве друга у нас есть только два варианта: либо подогнать свой код к случаю 1, либо - к случаю 3. В нашем случае это выглядит следующим образом.

исходный текст корректен, так как соответствует случаю 3 friend void boost::checked delete( Test* x );

добавляем <Test> ; код при этом остается корректен, но соответствует случаю 1

friend void boost::checked delete<Test>( Test* x );

Первый вариант по сути представляет собой сокращение второго... но только если имя квалифицировано (в нашем случае - использованием boost::) и в указанной области видимости нет подходящих нешаблонных функций. Хотя оба объявления корректны, первое использует то, что я называю темными углами языка, в них часто путаются не только программисты, но и компиляторы. Я могу назвать по крайней мере три причины, по которым следует избегать объявления такого вида, несмотря на его техническую корректность.

Причина 1: не всегда работает

Как уже упоминалось, синтаксис, используемый в третьем правиле, по сути представляет собой сокращение синтаксиса, используемого в первом правиле; в нем опускаются явные аргументы шаблона в угловых скобках. Однако такое сокращение работает только тогда, когда имя квалифицировано и указан класс или пространство имен, которые не содержат подходящей нешаблонной функции.

В частности, если пространство имен содержит (или получит позже) подходящую нешаблонную функцию, то будет выбрана именно она, поскольку ее наличие означает, что реализуется случай 2, а не 3. Достаточно тонко и неожиданно, не правда ли? Здесь очень легко допустить ошибку, так что лучше избегать использования таких тонкостей.

Причина 2: удивляет программистов

Случай 3 оказывается неожиданным и удивительным для программистов, которые пытаются разобраться в коде и понять, как он работает. Рассмотрим, например, весьма незначительно отличающийся вариант кода - все отличие состоит в том, что я убрал квалифицирующую часть boost::.

имя стало неквалифицированным, а это означает нечто

совершенно иное

class Test {

-TestО { }

friend void checked delete( Test* x );

Если вы опустите boost:: (т.е. сделаете вызов неквалифицированным), то оказывается, что эта ситуация соответствует совсем другому случаю, а именно - случаю 4, который вообще не работает для шаблонов. Держу пари, что любой программист



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

Причина 3: удивляет компиляторы

Случай 3 оказывается неожиданным и удивительным для компиляторов, он может оказаться неприменимым па практике, даже если мы проигнорируем приведенные ранее причины отказаться от данного способа.

Испытаем коды, относящиеся к случаям I и 3, на разных компиляторах, и проанализируем результат. Воспринимают ли компиляторы стандарт так же, как и мы (дочитавшие книгу до этого места)? Будут ли, по крайней мере, самые мощные компиляторы вести себя так, как мы от них ожидаем? Оба ответа отрицательны...

Сначала обратимся к случаю 3.

пример 8-1 - еще раз

namespace boost {

tempiate<typename т> void checked delete( T* x ) { ... остальной код ... delete x;

class Test { -TestO { } изначальный код

friend void boost::checked deleteC Test* x );

i nt mainO {

boost::checked delete( new Test );

Попробуйте скомпилировать этот код на вашем компиляторе, и мы сравним наши результаты (см. табл. 8.1).

Таблица 8.1. Результат компиляции примера 8-1 разными компиляторами

Компилятор

Результат

Сообщение об ошибке

Borland 5.5

Comeau 4.3.0.1

Digital Mars 8.38

Error

Symbol Undefined ?checkcd.....dclete@@YAXPAV-

Test@@@Z (void cdecl checked.....dclctc(Tc5t *))

EDG 3.0.1

Intel 6.0.1

gcc 2.95.3

Error

~boost::checked delete(Test *) should have been declared inside boost

gcc .3.4

Error

void boost::checked delete(Test*) should have been declared inside boost

Metrowerks 8.2

Error

friend void boost;:checked delete( Test* x ); name has not been declared in namespace/class

MS VC++ 6.0

Error

nonexistent function boost::checked deiete specified as friend

MS VC++ 7.0 (2002)



Компилятор

Результат

Сообщение об ошибке

MS VC++ 7.1 (2003)

Error

boost;:checked de!ete : not a function

MS VC++ 8.0 (2005) beta

Итак, наш тест показывает, что этот синтаксис плохо распознается современными компиляторами, один из них даже неоднократно меняет свое мнение по поводу данного кода в процессе разработки новых версий. Кстати, нас не должно удивлять, что компиляторы Comeau, EDG и Intel скомпилировали этот код, поскольку все они основаны на реализации EUG С++. Получается, что из пяти различных реализаций языка три (Digital Mars, gcc и Metrowerks) не понимают такой синтаксис, два других (Borland и EDG) понимают, и еще один никак не может определиться (Microsoft).

Теперь изменим исходный текст так, чтобы он относился к случаю 1.

пример 8-2: другой способ объявления друга

namespace boost {

tempiate<typename т> void checked de1ete( т* x ) { ... остальной код ... delete x;

class Test { -TestO { }

Альтернативный способ

friend void boost::checked de1ete<> ( Test* x );

int mainC) {

boost::checked delete( new Test );

Можно использовать и эквивалентную форму записи

friend void boost::checked delete<Test>( rest* x );

Результат исследований более оптимистичен - такой код понятен уже для большего количества компиляторов (см. табл. 8-2.).

Таблица 8.2. Результат компиляции примера 8-1 разными компиляторами

Компилятор

Результат

Сообщение об ошибке

Borland 5.5

Comeau 4.3.0.1

Digital Mars 8.38

EDG 3.0.1

Intel 6.0.1

gcc 2.95.3

Error

boost::checkcd dclete(Tesl *) should have been declared inside boost

gcc 3.1.1

Error

void boost::checked de!ete(Test*) should have been declared inside boost

gcc 3.4

Error

void boost::chcckcd dcletc(Test*) should have been declared inside boost



1 ... 11 12 13 [ 14 ] 15 16 17 ... 84

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика