模板参数函数必须被视为潜在的constexpr吗?

30 c++ templates language-lawyer constexpr c++11

考虑这个程序:

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

这有效吗?编译器是否需要查看模板定义时间,这F可能会引用一个constexpr函数,因此默认参数N可能有效吗?

gcc和clang接受这个,但是Intel 1在模板定义时拒绝模板函数,因为F()它不是一个常量表达式.f<g, g()>()如果默认参数被删除,英特尔确实接受,因此很明显它理解它g()通常可用于常量表达式.

我不清楚标准说的是什么.很明显(C++ 11 [expr.const] p2)

调用除constexpr文字类或constexpr函数的构造函数之外的函数

呈现一个非常量的表达式,但我不清楚这是否适用于此.在模板定义时,它肯定似乎适用,因为F未声明为constexpr函数,但同时,模板定义时的错误应该仅在模板没有可能的有效实例化时被诊断出来,并且这里似乎确实存在有效的实例化.

我可以看到两个答案的论据,所以我感到困惑.这个问题有明确的答案吗?

1.使用当前版本的英特尔编译器重新测试表明它的工作正常,因此可能是英特尔开发人员认为它是一个错误并且已经修复了它.这是一个巨大的暗示,代码是有效的.尽管如此,根据标准得到一个确定的答案仍然会很好.

Fil*_*efp 7

介绍

template<int F(), int N = F()> void func ();
Run Code Online (Sandbox Code Playgroud)

在这个答案中,我们将逐步通过国际标准的相关部分,以证明上述片段是格式良好的.


国际标准(N3337)说什么?

Standardese

14.1p9 模板参数 [temp.param]

默认模板参数是一个模板参数之后指定(14.3)=模板参数.[...]

14.3p6 模板参数 [temp.arg]

如果模板参数的使用在模板专业化的实例化中产生了不正确的构造,则该程序是不正确的.

14.3.2p1 模板非类型参数 [temp.arg.nontype]

模板参数的用于非类型,非模板模板参数应是以下之一:

  • 用于非类型模板参数整数或枚举类型,类型的转换后的常量表达式(5.19)的模板的参数 ; 要么
  • 非类型模板参数的名称 ; 要么
  • 一个常量表达式(5.19),用于指定对象的地址[...] ; 要么
  • 一个常量表达式,其值为空指针值(4.10); 要么
  • 一个常量表达式,其值为null成员指针值(4.11); 要么
  • 指向成员的指针,如5.3.1中所述

5.19p3 常量表达式 [expr.const]

常量表达式是文本类型的prvalue核心常量表达式,但是没有指针类型.一个积分常量表达式是整体的或无作用域枚举类型的字面常量表达式.甲转换常量表达式类型的T是一个常量表达式,隐式转换为类型T,[...]

8.3.6p3 默认参数 [dcl.fct.default]

默认参数只能在函数声明的parameter-declaration-clausetemplate-parameter(14.1)中指定; 在后一种情况下,initializer子句应该是赋值表达式.


判决

以上部分使我们得出以下结论:

  • 一个默认的模板参数是一个模板参数,和;
  • 在实例化模板时,所有模板参数必须在它们出现的上下文中可用,并且;
  • 出现在程序中的非类型非模板模板参数的每个模板参数必须是文字常量表达式,并且;
  • 默认参数模板参数必须是一个赋值表达式.

说明

template<int F(), int N = F()>
void func ();
Run Code Online (Sandbox Code Playgroud)

constexpr int (*F)() = <some_initializer>;                    // (A)
constexpr int N      = <explicit_template_argument> OR <F()>  // (B)
Run Code Online (Sandbox Code Playgroud)

上面的代码段可以用作心理助手,以便在给定一组模板参数的情况下简化与模板参数等效的推理.

看有无(B)是否有效,其中一个明确的模板参数是不是给定ñ,我们必须评估(A) -和评价(A)可能生成一个值˚F那就是在使用(B)所要求的表达.

照这样说; 是的,模板是合法的C++ 11.

法律

constexpr int g () { ... }
Run Code Online (Sandbox Code Playgroud)

// func<&g> 
constexpr int (*F)() = &g;  // ok
constexpr int N      = F(); // ok
Run Code Online (Sandbox Code Playgroud)

病态的

          int f () { ... }
Run Code Online (Sandbox Code Playgroud)

// func<&f>
constexpr int (*F)() = &f;  // ok
constexpr int N      = F(); // ill-formed, not a constant-expression
Run Code Online (Sandbox Code Playgroud)

奖金

同一组规则适用于以下模板;

template<int X, int N = 100/X>
void gunc ();
Run Code Online (Sandbox Code Playgroud)

gunc<0> (); // ill-formed, `100/0` is not mathematically defined,
            //              and is therefore not a constant-expression
Run Code Online (Sandbox Code Playgroud)


对于语言律师

而这一点,毫无意义地使用默认模板参数,实际上是合法的,因为它F()可能是一个常量表达式.

F()然而,不能转换的常量表达式N一个值,但直到(如果有的话)实际使用默认参数时才会发生这种情况.

template<void F(), int N = F()>
void hunc ();
Run Code Online (Sandbox Code Playgroud)

void f ();

hunc<&f, 10> (); // legal
hunc<&f    > (); // ill-formed
Run Code Online (Sandbox Code Playgroud)