【问题标题】:What are the differences between 0, int() and int{}?0、int()和int{}有什么区别?
【发布时间】:2013-10-01 16:59:28
【问题描述】:

由于int()int{} 是值等于0constant 表达式,我认为它们是等价且可互换的,因此编译器必须平等对待它们。例如,

 int a[0];      //error: zero-sized array not allowed in ISO C++
 int b[int()];  //error: zero-sized array not allowed in ISO C++
 int c[int{}];  //error: zero-sized array not allowed in ISO C++

但似乎在某些极端情况下它们是不可互换的。

  • 初始化指针时:

    int *p = 0;     //ok
    int *q = int(); //error - by clang only
    int *r = int{}; //error - by gcc and clang both
    

    查看GCCClang 消息。我怀疑这是两个编译器中的一个错误,因为我希望它们在这种情况下可以互换,但我很高兴被证明是错误的。 :-)

  • 当传递给类模板时:

    template<int N> struct X{};
    
    X<0>      x1; //ok
    X<int{}>  x2; //ok (same as X<0>)
    X<int()>  x3; //error  
    

    查看GCCClang 消息。

    我发现X&lt;int()&gt; 的语法非常熟悉,因为我以前见过(并且可能使用过)类似的语法,例如在std::function&lt;int()&gt; 中,模板参数int() 预计为函数类型(而不是 0)不接受任何参数并返回 int。但我想知道在这种情况下int() 被视为函数 type 的规范部分,并不等同于始终为0int{}

【问题讨论】:

  • 所以这基本上是一个“grep the standard for me”的问题,对吧?
  • 我也喜欢。这是一个很好的问题——我的印象是标准并不清楚(但我还在研究它)。
  • @SteveJessop 你认为最棘手的解析是否适用于这里的任何情况?我没看到,除了X&lt;int()&gt;。即便如此,我认为编译器首先找到X,因此知道它不是在寻找类型。
  • 关于X&lt;int()&gt;: 14.3 [temp.arg] "在 template-argument 中,type-id 之间的歧义 并且一个 expression 被解析为一个 type-id,而不管对应的 template-parameter 的形式。”跨度>
  • @SteveJessop 是的。那个对我来说似乎也有问题。 C++ 通常会尽量避免需要语义信息来驱动解析。 (它并不总是成功。)在X&lt;int()&gt; 的情况下,语法上下文 允许类型声明,因此除非标准要求编译器获取语义信息(模板参数的事实不是类型参数),它是一个类型声明(也是最令人头疼的解析的另一个实例。

标签: c++ gcc clang zero compile-time-constant


【解决方案1】:

表达式int()int{} 都是整数类型的常量表达式纯右值,其计算结果为零,因此在任何需要计算的整数类型的整数常量表达式纯右值的上下文中都可以与文字0 互换归零。

两个表达式都满足5.19 常量表达式[expr.const]中指定的常量表达式的要求。

关于X&lt;int()&gt;,标准规定int()在此上下文中不被解释为表达式:

14.3 模板参数 [temp.arg]

template-argument 中,type-idexpression 之间的歧义被解析为 type-id ,不管对应的template-parameter的形式是什么

关于指针转换:

4.10 指针转换 [conv.ptr]

空指针常量是整数类型的整数常量表达式 (5.19) prvalue,其计算结果为零 或std::nullptr_t 类型的纯右值。

基于上一段,int()int{} 都是空指针常量表达式。这指向了一个(非常小的)编译器错误,尽管有一个打开的缺陷报告 (903) 可能导致这一段发生变化:

CWG 达成了强烈的共识,即只有文字 0 应被视为空指针常量,而不是当前指定的任意零值常量表达式。

以下措辞涉及表达式int() 的值:

8.5 初始化程序 [dcl.init]

对 T 类型的对象或引用进行零初始化意味着:

[省略的不适用的子句]

——如果T是标量类型(3.9),则对象设为0(零),取整型常量表达式, 转换为 T

[...]

对 T 类型的对象进行值初始化意味着:

——如果 T 是一个(可能是 cv 限定的)类类型(第 9 条),带有一个用户提供的构造函数(12.1),那么 调用 T 的默认构造函数(如果 T 没有可访问的默认值,则初始化格式错误 构造函数);

[省略的不适用的子句]

否则,对象被零初始化。

初始化器为空括号集的对象,即 (),应进行值初始化。

对于int{}的值:

8.5.4 列表初始化[dcl.init.list]

类型 T 的对象或引用的列表初始化定义如下:

——如果初始化列表没有元素并且 T 是具有默认构造函数的类类型,则对象为 值初始化。

[省略的不适用的子句]

否则,如果初始化列表没有元素,则对象被值初始化。

来自 C++ 工作草案标准 N3337 的所有引用。

【讨论】:

  • prvalue的定义见3.10 [basic.lval]。此定义包括文字 0
  • CWG 意见的改变,因为之前提出了整数表达式是有效的空指针常量的问题。那时我们明确允许false 作为 NPC。但不是1-1,因为它早于constexpr,因此我们无法表达什么是“足够的常量”。
  • @MSalters 在 DR 903 中有一条附加说明:在波特兰(2012 年 10 月)会议上提出了有关值 false 已在现有​​代码中用作空指针常量的担忧,并且此类代码将被此更改破坏。此问题已返回“审查”状态,以便讨论是否容纳此类代码。
猜你喜欢
  • 1970-01-01
  • 2019-11-06
  • 1970-01-01
  • 2020-05-05
  • 2015-11-04
  • 1970-01-01
  • 2014-10-20
  • 2017-02-23
  • 2021-04-02
相关资源
最近更新 更多