Программирование >>  Полиморфизм без виртуальных функций в с++ 

1 ... 118 119 120 [ 121 ] 122 123 124 ... 144


Было бы неразумно требовать, чтобы проектировщик класса предоставлял все функции, которые в будущем, возможно, пригодятся автору щаблона. Предвидеть все невозможно.

Поэтому понятие зависит от аргумента щаблона Т должно опираться на контекст в точке инстанцирования, по крайней мере, в такой степени, чтобы уметь находить глобальные функции, используемые вместе с Т. При этом неизбежно появляется возможность случайно подобрать какую-нибудь лишнюю функцию. Но эта проблема не слишком серьезна. Мы определим зависимость от аргумента шаблона Т наиболее стандартным способом. Именно так, вызов функции зависит от аргумента шаблона, если был разрешен по-другому или вообще не разрешен в случае отсутствия в программе фактического типа шаблона. Для компилятора проверить такое условие относительно просто. Вот примеры вызовов, зависящих от аргумента шаблона Т:

□ у вызванной функции есть формальный параметр, зависящий от Т в соответствии с правилами выведения типа (см. раздел 15.6.1). Например, f (Т), f(vector<T>), f(const Т*);

□ тип фактического аргумента зависит от Т в соответствии с правилами выведения типа(см.раздел 15.6.1).Так, f (Т(1) ),f (t),f (g(t) ) и f (&t),ecли предположить, что t - это Т;

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

Последний пример взят из реальной программы, и зависящий от этого правила код был вполне удачен. Вызов f (1), на первый взгляд, не зависит от Т, как не зависит от Т и функция f (В), к которой идет обращение. Но тип Т аргумента шаблона имел конструктор из типа int и являлся производным от В, поэтому f (1) разрешалось как f (В (Т (1) ) ).

15.10.2.2. Неоднозначности

Что надо было бы сделать, если в точке #1 (точка определения шаблона в примере из раздела 15.10.2) и в точке #2 (точка использования) обнаруживаются различные функции? Мы могли бы:

□ отдать предпочтение #1;

□ предпочесть #2;

□ выдать ошибку.

Отметим, что в точке #1 можно искать только не-функции и функции, для которых типы аргументов уже известны в точке использования в определении шаблона. Поиск же остальных имен откладывается до точки #2.

Первоначальное правило требует предпочесть точку #2, откуда следует, что применимы обычные требования разрешения неоднозначности. Ведь только в случае, когда в точке #2 найдено лучшее соответствие, могут возникнуть расхождения с тем, что найдено в точке #1. К сожалению, при этом приходится не верить собственным глазам, читая определение шаблона. Например:



double sqrt(double);

template<class T> void f(T t) {

double sq2 = sqrt(2); . . .

Кажется очевидным, что sqrt (2) вызовет sqrt (double). Ho в точке #2 вполне может обнаружиться функция sqrt (int). В большинстве случаев это неважно, так как правило должно зависеть от аргумента шаблона гарантирует использование именно очевидного разрешения в пользу sqrt (double). Однако если бы Т был равен int, то вызов sqrt (2) зависел бы от аргумента шаблона, так что вызов разрешился бы в пользу sqrt (int). Это неустранимое следствие того, что мы принимаем во внимание точку #2, но, по-моему, оно вызывает много путаницы. Хотелось бы как-то решить эту проблему.

С другой стороны, я считал необходимым отдавать предпочтение именно точке #2, ибо только тогда можно разрешить использование членов базового класса таким же способом, как при работе с обычными (нешаблонными) классами. Например:

void g();

template<class Т> class X : public Т { void f() { g(); } ...

Если в Т есть функция-член g (), следовало бы вызывать именно эту g (), поскольку так ведут себя нешаблонные классы:

void g() :

class Т { public: void g(); };

class Y : public T {

void f() { g(); } вызывается T::g ...

С другой стороны, в самых типичных случаях то, что найдено в точке # 1, обычно корректно. Так работает в C-i-i- поиск глобальных имен, именно такая модель позволяет на ранних стадиях обнаруживать большую часть ошибок, предварительно компилировать большинство шаблонов и именно такой механизм защищает от случайного заимствования имен в контексте, неизвестном автору шаблона. Несколько разработчиков компиляторов, особенно Билл Гиббоне, убедительно доказывали, что предпочтение следует отдать точке #1.

Какое-то время я склонялся к тому, чтобы считать ошибкой нахождение разных функций в двух данных точках, но это лишь усложняет задачу разработчиков



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

double sqrt(double);

template<class Т> void f(T t) {

...

sqrt(2); разрешается в точке #1

sqrt(T(2)); очевидно зависит от Т привязка в точке #2

int g();

template<class Т> class X : public Т ( void fO {

g(); разрешается в точке #1

T::g(); очевидно зависит от Т привязка в точке #2

...

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

15.103. Специализация

Шаблон описывает, как определяется функция или класс при любых значениях его аргументов. Например, шаблон

template<class Т> class Comparable { .. .

int operator==(const T& a, const T& b) { return a==b; }

означает, что для каждого типа Т элементы сравниваются с помощью оператора ==. К сожалению, это слишком ограничительное условие. В частности, в С строки, представленные типом char*, обычно сравниваются функцией strcmpO.



1 ... 118 119 120 [ 121 ] 122 123 124 ... 144

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