Ответ 1
Введение
template<int F(), int N = F()> void func ();
В этом ответе мы шаг за шагом рассмотрим соответствующие разделы международного стандарта, чтобы доказать, что приведенный выше фрагмент хорошо сформирован.
Что говорит Международный стандарт (N3337)?
Стандартная
14.1p9 Параметры шаблона
[temp.param]
Шаблон-аргумент по умолчанию - это шаблон-аргумент (14.3), указанный после
=
в шаблоне-параметре. [...]
14.3p6 Аргументы шаблона
[temp.arg]
Если использование аргумента шаблона приводит к плохо сформированной конструкции при создании шаблонной специализации, программа плохо сформирована.
14.3.2p1 Шаблоны без аргументов
[temp.arg.nontype]
Аргумент шаблона для непигового шаблона-шаблона без шаблона должен быть одним из следующих:
- для нетипового шаблона-параметра интегрального или перечисляемого типа, преобразованного константного выражения (5.19) типа шаблона-параметра; или
- имя несимметричного шаблона; или
- константное выражение (5.19), которое обозначает адрес объекта [...]; или
- константное выражение, которое вычисляет значение нулевого указателя (4.10); или
- константное выражение, которое вычисляет значение указателя нулевого элемента (4.11); или
- указатель на элемент, выраженный как описано в 5.3.1
5.19p3 Константные выражения
[expr.const]
Литеральное постоянное выражение является выражением константы основного значения prvalue тип литерала, но не тип указателя. Интегральное постоянное выражение представляет собой литеральное постоянное выражение интегрального или неперечисленного типа перечисления. Преобразованное константное выражение типа
T
является литеральным постоянным выражением, неявно преобразованный в типT
, [...]
8.3.6p3 Аргументы по умолчанию
[dcl.fct.default]
Аргумент по умолчанию должен указываться только в объявлении параметра-объявления объявления функции или в параметре шаблона (14.1); в последнем случае предложение initializer должно быть выражением присваивания.
Вердикт
В приведенных выше разделах мы делаем следующие выводы:
- Шаблон-аргумент по умолчанию - это шаблон-аргумент и
- при создании экземпляра шаблона все шаблонные аргументы должны использоваться в контексте, где они появляются, и;
- каждый шаблон-аргумент для не-типа шаблона-шаблона без шаблона, который появляется в программе, должен быть литеральным постоянным выражением и;
- аргумент по умолчанию для параметра шаблона должен быть выражением присваивания.
Объяснение
template<int F(), int N = F()>
void func ();
constexpr int (*F)() = <some_initializer>; // (A)
constexpr int N = <explicit_template_argument> OR <F()> // (B)
Фрагмент выше можно использовать в качестве умственного помощника, чтобы облегчить рассуждение о том, что параметры шаблона будут эквивалентны, учитывая набор шаблонов-аргументов.
Чтобы убедиться, что (B) является допустимым или нет, где явный шаблон-аргумент не указан для N, мы должны оценить (A) - и оценка (A) может дать значение для F, которое можно использовать в константное выражение, требуемое (B).
Сказанное; Да, шаблон является законным С++ 11.
<суб > Юридическаясуб >
constexpr int g () { ... }
// func<&g>
constexpr int (*F)() = &g; // ok
constexpr int N = F(); // ok
<суб > Иллинойс сформированныйсуб >
int f () { ... }
// func<&f>
constexpr int (*F)() = &f; // ok
constexpr int N = F(); // ill-formed, not a constant-expression
Bonus
Тот же набор правил применяется к следующему шаблону:
template<int X, int N = 100/X>
void gunc ();
gunc<0> (); // ill-formed, `100/0` is not mathematically defined,
// and is therefore not a constant-expression
Для юриста по языку
И это, бессмысленное использование шаблона-аргумента по умолчанию, фактически является законным, поскольку F()
может быть константным выражением.
F()
может, однако, не быть преобразованным константным выражением, чтобы дать значение N, но это не происходит до тех пор, пока (если вообще) аргумент по умолчанию фактически не используется.
template<void F(), int N = F()>
void hunc ();
void f ();
hunc<&f, 10> (); // legal
hunc<&f > (); // ill-formed