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

Must template argument functions be treated as potentially constexpr?

考虑这个程序:

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

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

gcc 和 clang 接受这个,但是 Intel1 在模板定义时拒绝模板函数,因为 F() 不是常量表达式。如果删除默认参数,Intel 确实接受 f<g, g()>(),因此它清楚地理解 g() 通常可用于常量表达式。

我不清楚标准的内容。很明显 (C++11 [expr.const]p2)

an invocation of a function other than a constexpr constructor for a literal class or a constexpr function

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

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

1. 使用当前版本的英特尔编译器重新测试表明它工作正常,因此英特尔开发人员可能认为这是一个错误并且自从修好了。这是一个巨大的暗示,表明该代码是有效的。不过,如果能根据标准得到一个结论性的答案,那还是很好的。

简介

template<int F(), int N = F()> void func ();

在这个答案中,我们将逐步检查国际标准的相关部分,以证明上述代码段的格式正确。


国际标准 (N3337) 怎么说?

标准语

14.1p9 Template parameters [temp.param]

A default template-argument is a template-argument (14.3) specified after = in a template-parameter. [...]

14.3p6 Template arguments [temp.arg]

If the use of a template-argument gives rise to an ill-formed construct in the instantiation of a template specialization, the program is ill-formed.

14.3.2p1 Template non-type arguments [temp.arg.nontype]

A template-argument for a non-type, non-template template-parameter shall be one of:

  • for a non-type template-parameter of integral or enumeration type, a converted constant expression (5.19) of the type of the template-parameter; or
  • the name of a non-type template-parameter; or
  • a constant expression (5.19) that designates the address of an object [...]; or
  • a constant expression that evaluates to a null pointer value (4.10); or
  • a constant expression that evaluates to a null member pointer value (4.11); or
  • a pointer to member expressed as described in 5.3.1

5.19p3 Constant expressions [expr.const]

A literal constant expression is a prvalue core constant expression of literal type, but not pointer type. An integral constant expression is a literal constant expression of integral or unscoped enumeration type. A converted constant expression of type T is a literal constant expression, implicitly converted to the type T, [...]

8.3.6p3 Default arguments [dcl.fct.default]

A default argument shall be specified only in the parameter-declaration-clause of a function declaration or in a template-parameter (14.1); in the latter case, the initializer-clause shall be an assignment-expression.


判决书

以上几节让我们得出以下结论:

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

解释

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) 的评估可能会产生 [=91] 的值=]F 可用于 (B).

所需的 constant-expression

话虽如此; ,模板是合法的 C++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

奖金

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

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() 可以 not 是一个 converted constant-expression 来给出 N 一个值,但这不会发生,直到(如果有的话)实际使用 默认参数

template<void F(), int N = F()>
void hunc ();

void f ();

hunc<&f, 10> (); // legal
hunc<&f    > (); // ill-formed