Каковы требования к параметрам шаблона C ++? - PullRequest
8 голосов
/ 13 марта 2009

Если вы используете шаблон в C ++, который принимает целочисленное значение в качестве параметра, есть ли какие-либо требования к целочисленной переменной, используемой в качестве параметра, которые отличаются от того, была ли эта переменная использована в качестве параметра при вызове функции?

Это продолжение вопроса здесь . Я специально хочу обратиться, если есть разница переменных WRT, объявленных как "extern const int" для функций или шаблонов?

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

Ответы [ 3 ]

13 голосов
/ 13 марта 2009

Следующее от стандарта.

14.3.2.1:

Аргумент шаблона для нетипичного, не шаблонного параметра шаблона должен быть одним из:

  • целочисленная константа-выражение целого или перечислимого типа; или
  • имя нетипового шаблона-параметра; или
  • адрес объекта или функции с внешней связью, включая шаблоны функций и идентификаторы шаблонов функций, но исключая нестатические члены класса, выраженный как & id-выражение, где & является необязательным, если имя относится к функции или массиву или если соответствующий шаблон-параметр является ссылкой; или
  • указатель на член, выраженный как описано в 5.3.1.

5.19.1:

В некоторых местах C ++ требует выражения, которые оцениваются как интеграл или константа перечисления: как границы массива (8.3.4, 5.3.4), как выражения регистра (6.4.2), как длины битового поля (9.6), как инициализаторы перечислителя (7.2), как инициализаторы статического члена (9.4.2), а также как целочисленные или нетипизированные аргументы шаблонного перечисления (14.3).

 constant-expression:
            conditional-expression

Интегральное константное выражение может включать только литералы (2.13), перечислители, константные переменные или члены-статические данные целочисленных или перечислимых типов, инициализированных константными выражениями (8.5), нетиповые шаблонные параметры целочисленных или перечислимых типов и sizeof выражения. Плавающие литералы (2.13.3) могут появляться, только если они приводятся к целочисленным или перечислимым типам. Только преобразования типа в целое или перечисление Типы могут быть использованы. В частности, за исключением выражений sizeof, функции, объекты классов, указатели или ссылки не должны использоваться, а операторы присваивания, приращения, уменьшения, вызова функции или запятой не должны использоваться.

Что касается вашего предыдущего поста, я считаю, что суть в части "константные переменные ... инициализирована с помощью ..." (и я не думаю, что инициализированный внешне считается).

4 голосов
/ 13 марта 2009

Это должно быть целочисленное константное выражение. Это объясняется стандартным документом на 5.19:

Интегральное константное выражение может включать только литералы (2.13), перечислители, константные переменные или члены-статические данные целочисленных или перечислимых типов, инициализированных константными выражениями (8.5), нетиповые шаблонные параметры целочисленных или перечислимых типов и sizeof выражения. Плавающие литералы (2.13.3) могут появляться, только если они приводятся к целочисленным или перечислимым типам. Можно использовать только преобразования типов в целочисленные или перечислимые типы.

Обратите внимание, что «интеграл» - это еще один термин для «целого числа», но он не совпадает с «int». Например, char имеет целочисленный / целочисленный тип, но, очевидно, не является типом int. Итак, конкретно, разрешено следующее

  • 10 or 10L or anything like that
  • enum { THIS, OR, THAT };
  • int const this_one = 10;
  • sizeof(char)
  • конечно, любой другой параметр шаблона, как описано выше

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

Итак, по этим правилам, если у вас есть

extern const int SomeName;

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

MyGreatStack<int, 4> // 4 is now part of the type MyGreatStack<int, 4>!

Обратите внимание, что есть другие способы передачи SomeName в качестве аргумента. Однако все это может не быть принято целочисленным параметром шаблона. Вы можете принять вышеупомянутое с помощью ссылочного параметра, например

template<const int& V> struct NowItWorks { };

И он принял бы SomeName выше. Теперь вместо значения выбрано определенное местоположение, уникальное для всей программы (так как переменная имеет extern связь).

3 голосов
/ 13 марта 2009

Это всегда тот случай, когда значение int необходимо во время компиляции.

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

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

...