具体来说,以下是合法的 C++ 吗?
A类{}; 无效 foo(A*); 空栏(常量 A&); 诠释主要(无效) { 富(&A()); // 1 酒吧(A());// 2 }
它似乎可以正常工作,但这并不意味着它一定是合法的。是吗?
编辑 - 更改A&
为const A&
具体来说,以下是合法的 C++ 吗?
A类{}; 无效 foo(A*); 空栏(常量 A&); 诠释主要(无效) { 富(&A()); // 1 酒吧(A());// 2 }
它似乎可以正常工作,但这并不意味着它一定是合法的。是吗?
编辑 - 更改A&
为const A&
1:不允许取临时地址。Visual C++ 允许它作为语言扩展(语言扩展默认是打开的)。
2:这是完全合法的。
不,将非常量引用传递给临时对象是违反标准的。您可以使用 const 参考:
class A{};
void bar(const A&);
int main(void)
{
bar(A()); // 2
}
因此,虽然一些编译器会接受它,并且只要不使用分号后的内存它就可以工作,但符合标准的编译器不会接受它。
foo在完全符合标准的 C++ 中是不允许的,而bar是可以的。尽管有可能,foo会在编译时出现警告,而bar也可能会在编译时出现警告,也可能不会。
A()创建一个临时对象,除非绑定到引用(如bar中的情况)或用于初始化命名对象,否则该对象在创建它的完整表达式的末尾被销毁。为保存引用初始值设定项而创建的临时对象将持续存在,直到其引用范围结束。对于bar的情况,这就是函数调用,因此您可以完全安全地使用A inside bar 。禁止将临时对象(右值)绑定到非常量引用。同样禁止获取右值的地址(作为参数传递以初始化A为foo)。
简短的回答是肯定的。
如果该对象被函数作为 const 引用参数接收 - 因为您已经修改bar(const A&)
了方法,那么它是完全合法的。函数可以对对象进行操作,但是函数调用后对象会被破坏(可以取临时地址,但不能在函数调用后存储和使用——原因见下文)。
也是合法的foo(A*)
,因为临时对象在完全表达式结束时被销毁。然而,大多数编译器都会发出关于获取临时地址的警告。
原始版本bar(A&)
不能编译,从临时初始化非常量引用是违反标准的。
C++ 标准第 12.2 章
3 [...] 临时对象作为评估完整表达式 (1.9) 的最后一步被销毁,该完整表达式 (从词法上) 包含它们被创建的点。[...]
4 有两种情况,其中临时对象在与完整表达式结尾不同的点被销毁。第一个上下文是当表达式作为定义对象的声明符的初始值设定项出现时。在这种情况下,保存表达式结果的临时变量将持续存在,直到对象的初始化完成。[...]
5 第二个上下文是引用绑定到临时的。引用绑定到的临时对象或作为临时对象绑定的子对象的完整对象的临时对象将在引用的生命周期内持续存在,除非下面指定。临时绑定到构造函数的ctorinitializer (12.6.2) 中的引用成员将持续存在,直到构造函数退出。临时绑定到函数调用 (5.2.2) 中的引用参数将一直持续到包含调用的完整表达式完成为止。在函数返回语句 (6.6.3) 中对返回值的临时绑定将持续存在,直到函数退出。
完整表达式是不是另一个表达式的子表达式的表达式。
那些 A 对象将只存在直到执行到达分号。因此,调用是安全的,但不要尝试保存指针并在以后使用它。此外,编译器可能要求 bar 采用 const 引用。
看起来它可以工作,但它没有使用带有 Wall 选项的 g++ 编译,这是我得到的:
michael@hardy-lenovo:~/Desktop$ g++ -Wall a.cpp a.cpp:在函数'int main()'中:michael@hardy-lenovo:~/Desktop$ g++ -Wall a.cpp a.cpp:在函数“int main()”中: a.cpp:8:警告:获取临时地址 a.cpp:9:错误:从“A”类型的临时变量中“A&”类型的非常量引用无效初始化 a.cpp:4: 错误:在传递 'void bar(A&)' 的参数 1 michael@hardy-lenovo:~/Desktop$
看起来您将需要使用常量引用。
这是合法的。我们有时会使用它来提供我们可能想要忽略的默认值。
int dosomething(error_code& _e = ignore_errorcode()) {
//do something
}
error_code
在上述情况下,如果没有传递给函数,它将构造一个空的错误代码对象。
//2 你需要一个常量引用
for //1 我认为这是合法但没用的
完全合法。
该对象将在函数调用期间存在于堆栈中,就像任何其他局部变量一样。