为什么我可以在循环中重新初始化一个常量?

5

编译器对以下代码没有警告或错误。const限定符的含义被滥用了吗?显然,我不能在同一次循环迭代中重新分配它,但它似乎在每次迭代后都会重新分配。

示例代码:

for(int i = 0; i < 10; ++i)
{
  const int constant = i;
}

6
问题:这里的常量的生命周期是多久? - Tristan Brindle
4
每次循环都会创建并销毁一个新的"constant"。您并没有重新初始化任何内容。 - Jesper Juhl
3
C++没有定义重新初始化的概念。直观上,重新初始化意味着已经初始化的对象。然而,在C++中,初始化只能在创建对象时发生。这意味着在构造时为对象提供其状态或值。一个已经存在的对象不能被初始化。最接近的方法是给它赋一个新值。 - François Andrieux
1
@user393454 虽然这段代码在编译为C或C++时可能没有不同的行为,但有很多代码示例在两种语言中都是语法上有效的,但具有不同的语义。因此,在标记您使用的语言时,精确性非常重要。 - Jesper Juhl
2
提示:使用C和C++时要小心,它们是DV磁铁 - 不管公平与否。大多数情况下,只应该使用其中一个。即使帖子的问题存在于两者之间,解决方案也有所不同。对于那些同时使用两个标签并且两个标签都有意义的帖子,请务必提供详细的理由以避免麻烦。 - chux - Reinstate Monica
显示剩余11条评论
4个回答

10
你没有重新初始化它,你只是在每次循环迭代中对它进行了初始化。严格地说,在每次循环迭代中都会创建和销毁一个新的 int,但编译器可以任意处理,只要它看起来像这样行为即可。
* 在C++中,你实际上无法"重新初始化"对象,初始化只会在对象生命周期内发生一次。

这取决于你为什么要初始化它。这个例子没有做任何有用的事情,所以这是一个不好的实践,因为它毫无意义。在其他情况下,这可能是有意义的。 - tadman
@tadman 好的,但是假设我在循环中传递的是一个const引用到函数中。那么const限定符就很有用了,因此在每次迭代中初始化const引用是必要的,对吧? - user393454
@user393454,虽然const对程序员来说很有用,可以防止错误,但对于优化器来说却是无用的。原因在于语言中存在const_cast(包括C风格的转换)、mutable、指针/引用可能别名等。const很好,它可以防止错误,但它只对程序员有用,而不是优化编译器。 - Jesper Juhl
由你决定。这取决于你对同事开发者的信任程度。 - tadman
@JesperJuhl:在C语言中,这部分是部分正确的(现代工具链也可能会让你惊讶),但在C++中不是。现代编译器很好地利用了这些知识。如果您进行不适当的强制转换,例如取消限定对象,即触发UB,那么您就自己承担风险了。 - too honest for this site
显示剩余5条评论

8
如果遵循C标准(6.2.4对象的存储期),则对象的存储期决定了其生命周期。共有四种存储期:静态、线程、自动和分配。分配的存储在7.22.3中有描述。
当使用未指定存储类别说明符static且无链接的标识符声明对象时,该对象具有自动存储期,某些复合字面量也是如此。尝试从与该对象关联的线程以外的线程间接访问具有自动存储期的对象的结果是由实现定义的。
对于没有可变长度数组类型的这种对象,它的生命周期从进入与其关联的块开始,直到以任何方式结束执行该块为止。(进入一个封闭块或调用一个函数会暂停当前块的执行,但不会结束执行。)如果递归地进入该块,则每次创建该对象的新实例。对象的初始值不确定。如果为对象指定了初始化,则在块的执行中到达声明或复合字面量时都会执行初始化;否则,每次到达声明时,该值都变为不确定。
迭代语句是一个作用域严格限于其封闭块范围内的块。循环体也是一个作用域严格限于迭代语句的范围内的块。因此,在此循环语句中。
for(int i = 0; i < 10; ++i)
{
    const int constant = i;
}

循环体是一个块。变量constant具有自动存储期。每次递归执行该块时,都会创建变量的新实例。
在C++中,您可以添加存储类说明符static。在这种情况下,变量确实只会被初始化一次,因为它具有静态存储期(在C中,您不可以这样做,因为变量必须由常量表达式初始化)。
以下是演示程序。
#include <iostream>

int main() 
{
    for ( int i = 0; i < 10; ++i )
    {
        static const int constant = i;
        std::cout << "constant = " << constant << std::endl; 
    }

    return 0;
}

它的输出是

constant = 0
constant = 0
constant = 0
constant = 0
constant = 0
constant = 0
constant = 0
constant = 0
constant = 0
constant = 0

2

这里实际上并没有重新初始化。每次循环都会创建一个新的变量。

constant 局限于循环内部的块中。当块在给定迭代中完成并且控制返回到 for 时,constant 超出了作用域,因此不再存在。当 for 开始下一次循环迭代时,将创建并初始化一个新的 constant 实例。


1
这个变量在每次迭代时都会被初始化和销毁,因为它是循环中的局部变量

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接