通过引用函数传递新构造的对象是否合法?

时间:2020-03-05 18:59:10  来源:igfitidea点击:

具体来说,以下是合法的C ++吗?

class A{};

void foo(A*);
void bar(const A&);

int main(void)
{
    foo(&A());  // 1
    bar(A());  // 2
}

它似乎可以正常工作,但这并不意味着它一定合法。是吗?

编辑将A&更改为const A&

解决方案

回答

完全合法。

该对象将在函数调用期间存在于堆栈中,就像其他任何局部变量一样。

回答

看起来它可能会起作用,但是它没有通过Wall选项与g ++一起编译,这是我得到的:

michael@hardy-lenovo:~/Desktop$ g++ -Wall a.cpp
a.cpp: In function ‘int main()’:michael@hardy-lenovo:~/Desktop$ g++ -Wall a.cpp
a.cpp: In function ‘int main()’:
a.cpp:8: warning: taking address of temporary
a.cpp:9: error: invalid initialization of non-const reference of type ‘A&’ from a temporary of type ‘A’
a.cpp:4: error: in passing argument 1 of ‘void bar(A&)’
michael@hardy-lenovo:~/Desktop$

看起来我们将需要使用常量引用。

回答

那些A对象将仅存在,直到执行到达分号为止。因此,调用是安全的,但不要尝试保存指针并在以后使用它。同样,编译器可能要求bar接受const引用。

回答

这是合法的。我们有时会使用它来提供一个我们可能要忽略的默认值。

int dosomething(error_code& _e = ignore_errorcode()) {
    //do something
}

在上述情况下,如果没有将error_code传递给函数,它将构造一个空的错误代码对象。

回答

对于// 2,我们需要一个const引用

对于/ / 1我认为这是合法的,但没用

回答

不,将非常量引用传递给临时对象是违反标准的。我们可以使用const引用:

class A{};

void bar(const A&);

int main(void)
{
    bar(A());  // 2
}

因此,尽管有些编译器会接受它,并且只要在分号后不使用内存,它就可以工作,但合规的编译器不会接受它。

回答

1:不允许使用临时地址。 Visual C ++允许它作为语言扩展(默认情况下,语言扩展处于启用状态)。

2:这是完全合法的。

回答

在完全符合标准的C ++中不允许foo,而bar可以。尽管有机会,但foo会带有警告编译,而bar也可能会或者可能不会带有警告编译。

A()创建一个临时对象,除非绑定到引用(如bar所示)或者用于初始化命名对象,否则该临时对象将在创建该对象的完整表达式的末尾销毁。为保存引用初始化程序而创建的临时目录将一直保留到其引用范围的末尾。对于bar而言,这就是函数调用,因此我们可以完美安全地使用A内部bar。禁止将临时对象(是右值)绑定到非常量引用。同样,也禁止使用右值的地址(作为参数传递以初始化foo的A)。

回答

简短的答案是肯定的。

如果在修改了bar(const A&)方法后,该对象被函数作为const引用参数接收,则完全合法。函数可以在对象上操作,但是在函数调用后对象将被销毁(可以使用临时地址,但在函数调用后不得存储和使用该地址,请参见下面的原因)。

foo(A *)也是合法的,因为临时对象在全表达式结束时被销毁了。但是,大多数编译器都会发出有关获取临时地址的警告。

原始版本的bar(A&)不能编译,这是从临时初始化非常量引用的标准。

C++ standard chapter 12.2
  
  3 [...] Temporary objects are destroyed as the last step in evaluating the fullexpression (1.9) that (lexically) contains the point where they were created. [...]
  
  4 There are two contexts in which temporaries are destroyed at a different point than the end of the fullexpression. The first context is when an expression appears as an initializer for a declarator defining an object. In that context, the temporary that holds the result of the expression shall persist until the object’s initialization is complete. [...]
  
  5 The second context is when a reference is bound to a temporary. The temporary to which the reference is bound or the temporary that is the complete object to a subobject of which the temporary is bound persists for the lifetime of the reference except as specified below. A temporary bound to a reference member in a constructor’s ctorinitializer (12.6.2) persists until the constructor exits. A temporary bound to a reference parameter in a function call (5.2.2) persists until the completion of the full expression containing the call.
  A temporary bound to the returned value in a function return statement (6.6.3) persists until the function exits.

全表达式是不是另一个表达式的子表达式的表达式。