我知道下面写的代码是非法的
void doSomething(std::string *s){}
int main()
{
doSomething(&std::string("Hello World"));
return 0;
}
原因是我们不允许获取临时对象的地址。但我的问题是为什么?
让我们考虑以下代码
class empty{};
int main()
{
empty x = empty(); //most compilers would elide the temporary
return 0;
}
接受的答案here提及
“通常编译器会将临时和副本构造为两个对象,它们位于内存的完全相同的位置,并避免复制。”
根据声明可以得出结论,临时存在于某个内存位置(因此可能已经采用了它),编译器决定通过在同一位置创建一个就地对象来消除临时对象。暂时存在。
这是否与临时地址不相符?
我还想知道如何实现返回值优化。有人可以提供与RVO实施相关的链接或文章吗?
答案 0 :(得分:13)
&std::string("Hello World")
问题不在于std::string("Hello World")
会产生临时对象。问题是表达式std::string("Hello World")
是一个引用临时对象的右值表达式。
您不能获取右值的地址,因为并非所有的右值都有地址(并非所有的右值都是对象)。请考虑以下事项:
42
这是一个整数文字,它是一个主要表达式和一个右值。它不是一个对象,它(可能)没有地址。 &42
是荒谬的。
是的,右值可以引用一个对象,就像你的第一个例子中的情况一样。问题是并非所有的右值都引用了对象。
答案 1 :(得分:5)
[...]可以得出结论,临时存在于某个存储位置
根据定义:
因此,不需要非常精细的证据来证明临时具有地址。这是定义。
OTOH,您不只是获取地址,而是使用内置地址运算符。内置地址运算符的规范表明您必须具有左值:
&std::string()
格式错误,因为std::string()
是右值。在运行时,对此表达式的此求值将创建一个临时对象作为副作用,并且该表达式将生成一个引用所创建对象的右值。&(std::string() = "Hello World")
格式正确,因为std::string() = "Hello World"
是左值。根据定义,左值是指对象。此左值引用的对象与完全相同的临时这是规则。它不需要某些人正在制定的(不正确的,不合理的)理由。
答案 2 :(得分:4)
$ 5.3.1 / 2 - “一元&运算符的结果是指向其操作数的指针。操作数应为左值或限定值。”
表达式,如
99
A() // where A is a user defined class with an accessible
// and unambiguous default constructor
都是Rvalues。
$ 3.10 / 2 - “左值是指 对象或功能。一些右值 表达式 - 类或者的表达式 cv限定的类类型 - 也参考 objects.47)“
这是我的猜测:即使Rvalues可能占用存储空间(例如在对象的情况下),C ++标准也不允许使用其内容来保持内置类型的一致性
这里有一些有趣的事情:
void f(const double &dbl){
cout << &dbl;
}
int main(){
f(42);
}
表达式'42'是一个Rvalue,它绑定到'const double'的引用,因此它创建了一个double类型的临时对象。这个临时的地址可以在函数'f'中获取。但请注意,在'f'里面,这不是一个临时或Rvalue。当它被赋予诸如'dbl'之类的名称时,它被视为'f'中的左值表达式。
NRVO上的Here's(类似的)
答案 3 :(得分:3)
临时是C ++“rvalue”的一个例子。它应该纯粹代表其类型中的值。例如,如果您在程序中的两个不同位置编写42
,则42
的实例无法区分,尽管可能在不同时间位于不同位置。你不能拿地址的原因是你需要做一些事情来指定应该有一个地址,因为否则地址的概念在语义上是不清洁和不直观的。
你“做某事”的语言要求有点武断,但它使C ++程序更加清晰。如果人们习惯接受临时的话,那就太糟糕了。地址的概念与生命的概念密切相关,因此使“瞬时”值缺乏地址是有意义的。不过,如果你小心,你可以获得一个地址,并在标准允许的生命周期内使用它。
其他答案中有一些谬论:
“你不能取rvalue的地址,因为并非所有的rvalues都有地址。” - 并非所有左值都有地址。参与简单循环并且随后未使用的类型int
的典型局部变量可能被分配寄存器但没有堆栈位置。没有内存位置意味着没有地址。但是,如果您获取其地址,编译器将将分配一个内存位置。 rvalues也是如此,它可能绑定到const
个引用。 “42
的地址”可以这样获得:
int const *fortytwo_p = & static_cast<int const &>( 42 );
当然,;
之后地址无效,因为暂时性是暂时的,这可能会产生额外的指令,因为机器可能无意义地将42存储到堆栈中。
值得一提的是,C ++ 0x通过将 prvalue 定义为表达式的值来清理概念,独立于存储, glvalue 是存储位置与其内容无关。这可能是C ++ 03标准的首要目的。
“然后你可以修改临时,这是没有意义的。” - 实际上有副作用的临时工具对修改很有用。考虑一下:
if ( istringstream( "42" ) >> my_int )
这是一个很好的习惯用于转换数字并检查转换是否成功。它涉及创建一个临时函数,在其上调用一个变异函数,然后销毁它。
答案 4 :(得分:2)
可以采取,但一旦暂时不存在,你就会有一个悬空指针。
修改强>
对于downvoters:
const std::string &s = std::string("h");
&s;
是合法的。 s
是对临时的引用。因此,可以采用临时对象的地址。
<强> EDIT2 强>
绑定引用是它们绑定的别名。因此,对临时的引用是该临时的另一个名称。因此,上段中的第二个陈述成立。
OP的问题是关于临时性(就他所使用的词而言),他的例子是关于rvalues。这是两个截然不同的概念。
答案 5 :(得分:0)
一个原因是你的例子会给方法写入临时访问权限,这是没有意义的。
您提供的引用与此情况无关,它是具有初始值设定项的声明符中允许的特定优化。
答案 6 :(得分:0)
为什么要取一个临时非法的地址?
临时变量的范围仅限于某些特定方法或某些块,只要方法调用返回临时变量就会从内存中删除,因此如果我们返回内存中不再存在的变量的地址它没有任何意义。地址仍然有效,但该地址现在可能包含一些垃圾值。