|
Программирование >> Обобщенные обратные вызовы
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 разными компиляторами
Итак, наш тест показывает, что этот синтаксис плохо распознается современными компиляторами, один из них даже неоднократно меняет свое мнение по поводу данного кода в процессе разработки новых версий. Кстати, нас не должно удивлять, что компиляторы 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 разными компиляторами
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |