3

当我遇到以下代码时,我正在调试一个程序,我错误地键入了类似于以下内容:

//Original (wrong)
std::string first("Hello");
std::string second = first + second;

//Instead of this (correct)
std::string first("Hello");
std::string second = first + something_else;

显然我并没有尝试这样做(我想不出为什么有人会想要这样做),但这让我思考。它看起来不像原来的应该工作,我认为它是未定义的。确实,这是我问题的根源。

为了使问题更普遍,请考虑以下几点:

SomeType a;
SomeType b = a + b;

行为未定义仅仅是因为b尚未初始化(请参阅此答案)?

如果行为未定义,那么我真正的问题是,为什么?

这是否仅针对某些标准容器未定义,例如std::string,还是在更一般的意义上未定义(STL 类、用户定义的类、POD、基本类型)?

标准的哪一部分适用于此?

如有必要,假设这是 c++11。

4

3 回答 3

6

C++11 标准对新声明名称的范围有这样的说法:

3.3.2 声明点[basic.scope.pdecl]

名称的声明点紧跟在它的完整声明符(第 8 条)之后和它的初始化器(如果有的话)之前,除非下面提到。[ 例子:

int x = 12;
{ int x = x; }

这里第二个 x 用它自己的(不确定的)值初始化。—结束示例]

以前的 C++ 标准中有类似的措辞。

在我的脑海中,我能想到的一个基本原理是,该名称可以用于获取对象地址的初始化表达式中。

于 2012-04-28T00:03:57.610 回答
1

读取未初始化的变量可能会导致未定义的行为。

标准是这样说的:

初始化程序 [dcl.init]

…………

如果没有为对象指定初始化器,则该对象是默认初始化的;如果不执行初始化,则具有自动或动态存储持续时间的对象具有不确定的值。

于 2012-04-28T00:02:33.730 回答
0

原因很简单:因为语法是糖。看似简单的赋值,实际上是复制构造;表达式的右手被评估并传递给左手的复制构造函数。

SomeType b = a + b;

实际上是

SomeType b(a + b /*wat?*/);

这背后的部分动机是 RVO。改为考虑以下情况

SomeType a, b;
SomeType c = a + b;

c实际上可以作为用于构造返回值的temp对象转发。a.operator+(b)

SomeType SomeType::operator+(const SomeType& rhs) const
{
    SomeType temp(*this); // RVO will employ `c` here instead of a 4th object.
    ...
    return temp; // yeah, let's not and say we did.
}

请注意,您可以使用自己的地址:

inptr_t i = (intptr_t)&i;
void* ptr = &ptr;

http://ideone.com/GUJyio

于 2013-11-30T20:07:15.457 回答