Подтвердить что ты не робот

Должны ли функции аргументов шаблона рассматриваться как потенциально constexpr?

Рассмотрим эту программу:

template <int F(), int N = F()>
void f() { }
constexpr int g() { return 1; }
int main() { f<g>(); }

Это действительно? Должны ли компиляторы видеть во время определения шаблона, что F может потенциально ссылаться на функцию constexpr, и поэтому аргумент по умолчанию для N может быть действительным?

gcc и clang принимают это, но Intel 1 отклоняет функцию шаблона во время определения шаблона, потому что F() не является постоянным выражением. Intel принимает f<g, g()>(), если аргумент по умолчанию удаляется, поэтому понятно, что g() можно использовать в постоянном выражении вообще.

Мне непонятно, что говорит стандарт. Ясно, что (С++ 11 [expr.const] p2)

вызов функции, отличной от конструктора constexpr для литерала или функции constexpr

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

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

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

4b9b3361

Ответ 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