静态 constexpr 变量可以用作模板参数吗

can a static constexpr variable be used as a template argument

我有以下代码可以在较旧的 gcc 上编译,但不能在版本 6 上编译(适用于 -std=c++1z)。 Clang 也拒绝了它,说对象 val 没有正确的链接类型。我不明白其中的区别。指针类型的 constexpr 变量不是应该或多或少透明地工作吗?我在允许它工作的语法中缺少什么吗?或者这是否违反了标准的某些部分?

typedef void(*t_voidfn)();
template <t_voidfn> struct s {};
void fn() {
  static constexpr t_voidfn val = &fn;
  s<val> x;
}

另一方面,这个有效。

typedef void(*t_voidfn)();
template <t_voidfn> struct s {};
void fn() {
  s<&fn> x;
}

第一个片段在 C++17 中是正确的,但在 C++14 和 11 中不正确。

对于 C++14,[temp.arg.nontype]/1 表示:

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

[...]

  • a constant expression (5.19) that designates the address of a complete object with static storage duration and external or internal linkage or a function with external or internal linkage, including function templates and function template-ids but excluding non-static class members, expressed (ignoring parentheses) as & id-expression, where the id-expression is the name of an object or function, except that the & may be omitted if the name refers to a function or array and shall be omitted if the corresponding template-parameter is a reference; 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; or
  • a constant expression of type std::nullptr_t.

(我只包含了与指针和成员指针直接相关的项目符号。)

基本上,您示例中的函数地址必须严格表示为&fnfn

C++11 包含基本相同的措辞,只是在 11 和 14 之间的缺陷报告中引入了一些说明:

  • DR1570 澄清了关于完整对象的部分;
  • DR1398 amended by DR1666 添加了最后一个项目符号。

对于 C++17,由于采用了论文 N4268 (rationale in N4198),限制已经放宽。相应的第(2)段现在说:

A template-argument for a non-type template-parameter shall be a converted constant expression (5.20) of the type of the template-parameter. For a non-type template-parameter of reference or pointer type, the value of the constant expression shall not refer to (or for a pointer type, shall not be the address of):

  • a subobject (1.8),
  • a temporary object (12.2),
  • a string literal (2.13.5),
  • the result of a typeid expression (5.2.8), or
  • a predefined __func__ variable (8.4.1).

[ Note: If the template-argument represents a set of overloaded functions (or a pointer or member pointer to such), the matching function is selected from the set (13.4). — end note ]

N4198 包含对每个项目符号的很好解释。