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

1 ... 109 110 111 [ 112 ] 113 114 115 ... 144


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

Видимо, я недооценил важность ограничений для удобства чтения и раннего выявления ошибок, но, с другой стороны, обнаружились и дополнительные проблемы при выражении таковых: тип функции специфичен, чтобы быть эффективным ограничением. Если рассматривать тип функции буквально , то он слишком ограничивает решение. В примере с шаблоном класса vector логично ожидать, что подходит любая операция <, принимающая два аргумента типа Т. Однако кроме встроенного оператора < есть еще несколько разумных альтернатив:

int X::operatOr<(Х) ;

int Y::operator<(const Y&);

int operator<{Z,Z);

int operator<(const ZZ&, const ZZ&);

При буквальной интерпретации допустимым аргументом для vector является только вариант с ZZ.

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

□ некоторые пользователи полагают, что при наличии ограничений на аргументы шаблонов можно сгенерировать оптимальный код, - я в это не верю;

□ другие думают, что в отсутствие ограничений ставится под угрозу статическая проверка типов. На самом деле только некоторые этапы проверки откладываются до момента компоновки и на практике это является проблемой;

□ кто-то считает, что при наличии ограничений объявление шаблона было бы проше понять. Часто это действительно так.

В следующих пунктах описываются два способа выражения ограничения. В некоторых случаях альтернатива.ми ограничениям являются генерирование функций-членов, когда они действительно нужны (см. раздел 15.5), и специализация (см. раздел 15.10.3).

75.4.7. Ограничения за счет наследования

Дуг Леа, Эндрю Кениг, Филипп Готрон (Philippe Gautron), я и многие другие независимо обнаружили, как можно воспользоваться синтаксисом наследования для выражения ограничений. Например:

template <class Т> class Comparable { Т& operator=(const Т&); int operator==(const T&, const T&); int operator<=(const T&, const T&); int operator<(const T&, const T&);

template <class T : Comparable> class vector { . . .



Это имеет смысл. Предложения отличаются деталями, но позволяют перенести обнаружение ошибок и диагностику на этап компиляции отдельной единой трансляции. Возможные варианты пока обсуждаются в группе по стандартизации С++.

У меня, впрочем, есть принципиальные возражения против выражения ограничений с помощью наследования. Данная возможность будет подталкивать программистов к такой организации программ когда все то, что может являться разумны.м ограничением, выносится в класс, а наследование рассматривается как средство выражения любых ограничений. Например, вместо того чтобы сказать в классе т должен быть оператор меньше , придется говорить класс Т должен быть производным от класса СотрагаЫе . Это непрямой и негибкий способ выражения ограничений, который легко ведет к злоупотреблению наследованием.

Поскольку между встроенными типами (int, double и т.д.) нет отношений наследования, то использовать его для выражения ограничений на такие типы нельзя. Недопустимо также применение наследования для выражения ограничений, которые можно использовать одновременно со встроенным и определенным пользователе.м типами. Например, с помощью наследования нельзя сказать, что int и complex допустимы в качестве аргументов шаблона.

Далее, автор шаблона не может предвидеть всех возможных применений средства. Поэтому сначала на аргументы шаблона будут накладываться чересчур строгие ограничения, а затем - на основе опыта - они начнут излишне ослабляться. Логическим следствием использования метода ограничений за счет наследования будет введение универсального базового класса, выражающего идею отсутствия ограничений . Но наличие подобного базового класса породит небрежное отношение к программированию как в контексте использования шаблонов, так и в других ситуациях (см. раздел 14.2.3).

При.мепение наследования в качестве средства ограничений на аргументы шаблонов не позволяет программисту написать два шаблона с одним и тем же именем, один из которых используется для указательных, а другой - для неуказательных типов. Проблему - мое внимание к ней привлек Кит Горлен - можно решить с по.мощью перегрузки шаблонов функций (см. раздел 15.6.3.1).

Фундаментальное основание для критики этого подхода: наследование используется для того, что не является порождением подтипов. По-моему, наследование применяется для выражения ограничений не в силу глубоких внутренних причин. Отношения наследования - не единственные полезные отношения, имеющиеся в языке.

15.4.2. Ограничения за счет использования

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

template<class Т> class X { . . .

void constraints{Т* tp)

{ Т должен иметь:

В* bp = tp; доступный базовый класс В

tp->f{); функцию-член f



Т а(1); конструктор из int а = *tp; оператор присваивания . . .

К сожалению, здесь используется одна деталь, характерная для определенного компилятора: Cfront выполняет полный синтаксический и семантический контроль всех встраиваемых функций в момент инстанцирования объявленного шаблона. Однако вариант функции с конкретны.м набором аргументов не следует инстанцировать, если она фактически не вызывается (см. раздел 15.5).

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

С другой стороны, автор может вызывать функцию constraints () и сам из каждого конструктора. Но это утомительно, если конструкторов много и не все из них встраиваются.

Эту концепцию можно было бы фор.мализовать, введя в язык специальное средство:

template<class Т> { constraints {

Т* bp; Т должен иметь:

В* bp = tp; доступный базовый класс В

tp->f(); функцию-член f

Т а(1); конструктор из int

а = *tp; оператор присваивания

. . .

class X { ...

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

Примеры важных ограничений, выраженных функциями-членами шаблонов, см. в разделах 15.9.1 и 15.9.2.

15.5. Устранение дублирования кода

Устранение ненужного расхода 1ш.мяти, вызванного слишком большим числом инстанцирований, считалось первоочередной проблемой проектирования языка, а не деталью реализации. Правила, требующие позднего инстанцирования функций-членов шаблона (см. разделах 15.10 и 15.10.4), гарантируют, что код не будет



1 ... 109 110 111 [ 112 ] 113 114 115 ... 144

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