【问题标题】:Must template argument functions be treated as potentially constexpr?模板参数函数是否必须被视为潜在的 constexpr?
【发布时间】:2015-03-10 15:21:04
【问题描述】:

考虑这个程序:

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

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

gcc 和 clang 接受这一点,但 Intel1 在模板定义时拒绝模板函数,因为 F() 不是常量表达式。如果删除了默认参数,英特尔确实接受f&lt;g, g()&gt;(),因此它清楚地理解g() 通常可用于常量表达式。

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

对文字类或 constexpr 函数的 constexpr 构造函数以外的函数的调用

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

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

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

【问题讨论】:

  • 旁注:已调整为指向函数的指针。
  • 我猜这是不正确的,因为 F() 不是 constexpr 所以不符合 [temp.arg.nontype] 的“常量表达式”。跨度>
  • 可能相关? N4198
  • @T.C.当然。对函数的引用以相同的方式工作,FWIW:被 gcc 和 clang 接受,被 Intel 拒绝。
  • @dyp 嘿,很好。 template &lt;typename T&gt; int f(); template &lt;&gt; constexpr int f&lt;int&gt;() { return 0; } template &lt;typename T, int N = f&lt;T&gt;()&gt; int g() { return N; } int main() { return g&lt;int&gt;(); } 产生“内部错误:断言失败:“shared/cfe/edgcpfe/exprutil.c”,第 11056 行”。 :)

标签: c++ templates c++11 language-lawyer constexpr


【解决方案1】:

简介

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

在这个答案中,我们将通过国际标准的相关部分,一步一步地证明上述sn-p是合式的。


国际标准 (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]

只能在函数声明的parameter-declaration-clausetemplate-parameter (14.1) 中指定默认参数;在后一种情况下,initializer-clause 应该是一个 assignment-expression


判决

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

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

解释

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

constexpr int (*F)() = <some_initializer>;                    // (A)
constexpr int N      = <explicit_template_argument> OR <F()>  // (B)

上面的 sn-p 可以用作心理助手,以简化推理 template-parameters 将等价于什么,给定一组 template-arguments .

要查看 (B) 是否有效,在没有为 N 给出显式 模板参数 的情况下,我们必须评估(A) - 对 (A) 的评估可能会为 F 生成一个值,该值可用于 constant-expression (B) 需要。

话虽如此; 是的,模板是合法的 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-expressionN 一个值,但这不会发生,直到 (如果有)实际使用了 默认参数

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

void f ();

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

【讨论】:

  • 您的奖金提供了一个有趣的案例,即 GCC 和 clang 不同意。 template &lt;int = 1/0&gt; void f() { } int main() { f&lt;0&gt;(); } 有效与否? GCC 是这么认为的(即使使用-std=c++11 -pedantic-errors 也接受它),无论命令行选项如何,clang 都会拒绝它。至于您的其余答案,谢谢,您提出了一些有趣的观点。我还不完全相信,但我会重新阅读一下,看看我是否遗漏了你已经解决的问题。
  • @hvd 我很快就会再补充一点,template&lt;int = 1/0&gt; void f; 格式错误 因为1/0 不是 常量-expression(而F(),在前面的例子中,可以是一个constant-expression)。
  • 但是你的回答并没有说模板默认参数必须是常量表达式,这确实不是一个合理的要求,因为100/X 不是模板定义时的常量表达式,它仅在实例化期间用非零值替换X变为常量表达式。在我的示例中,这永远不会发生,因为唯一的模板实例化显式指定了一个参数。
  • @hvd 注意使用"converted constant-expression",这表示表达式本身必须是constant-expression;我将通过将标准的相关部分添加到答案中来更明确地说明这一点。
  • 我当然同意f&lt;&gt;f&lt;1 / 0&gt; 是无效的。您引用的内容确实排除了这一点:模板参数必须是(转换后的)常量表达式,并且 1 / 0 不合格。但是在template &lt;int = 1/0&gt; void f() { } 中,没有具有非常量值的模板参数,因为模板默认参数不是模板参数,除非它最终被使用。 (N3337 错误地谈到了模板参数的要求,在后面的草案中已修复为引用模板参数。)
猜你喜欢
  • 2011-09-15
  • 1970-01-01
  • 2017-02-05
  • 2016-04-28
  • 1970-01-01
  • 1970-01-01
  • 2016-07-15
  • 2023-03-16
  • 1970-01-01
相关资源
最近更新 更多