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

时间:2008-09-17 15:23:57

标签: c++

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

class A{};

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

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

它似乎工作正常,但这并不意味着它必然是合法的。是吗?

修改 - 将A&更改为const A&

9 个答案:

答案 0 :(得分:16)

1:不允许使用临时地址。 Visual C ++允许它作为语言扩展(默认情况下语言扩展是打开的)。

2:这是完全合法的。

答案 1 :(得分:9)

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

class A{};

void bar(const A&);

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

因此,虽然一些编译器会接受它,并且只要在分号后不使用内存就可以工作,但是符合标准的编译器不会接受它。

答案 2 :(得分:6)

完全符合标准的C ++中不允许使用 foo ,而 bar 是可以的。虽然有可能, foo 会编译并发出警告,而 bar 也可能会编译也可能不会发出警告。

A()创建一个临时对象,除非绑定到引用(如 bar 中的情况),或用于初始化命名对象,否则会被销毁在创建它的完整表达式的末尾。创建用于保存引用初始值设定项的临时值会持续到其引用范围的末尾。对于 bar 的情况,这是函数调用,因此您可以完全安全地使用 bar 中的 A 。禁止将临时对象(这是一个右值)绑定到非const引用。同样禁止使用rvalue的地址(作为参数传递给 foo 初始化 A )。

答案 3 :(得分:2)

简短的回答是肯定的。

如果函数接收到对象作为const引用参数 - 因为您修改了bar(const A&)方法,那么它是完全合法的。函数可以对对象进行操作,但是在函数调用之后对象将被破坏(临时的地址可以被占用,但是在函数调用之后不能存储和使用 - 参见下面的原因)。

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

bar(A&)的原始版本不能编译,它违反标准来初始化临时的非const引用。

  

C ++标准第12.2章

     

3 [...]临时对象被破坏作为评估全文(1.9)的最后一步,它(词汇上)包含创建它们的点。 [...]

     

4有两种情况,临时表演在不同于完整表达结束时被摧毁。第一个上下文是表达式作为定义对象的声明符的初始值设定项。在该上下文中,保存表达式结果的临时值将持续存在,直到对象的初始化完成。 [...]

     

5第二个上下文是指引用绑定到临时的。除了下面指定的内容之外,引用绑定的临时对象或作为临时绑定对象的子对象的完整对象的临时对象的生命周期仍然存在。绑定到构造函数的ctorinitializer(12.6.2)中的引用成员的临时绑定将持续存在,直到构造函数退出。在函数调用(5.2.2)中与引用参数的临时绑定将持续存在,直到包含该调用的完整表达式完成为止。   函数返回语句(6.6.3)中返回值的临时绑定将一直存在,直到函数退出。

fullexpression 是一个表达式,它不是另一个表达式的子表达式。

答案 4 :(得分:1)

这些A对象只有在执行到达分号后才会存在。因此,调用是安全的,但不要尝试保存指针并在以后使用它。此外,编译器可能需要bar采用const引用。

答案 5 :(得分:0)

看起来它会起作用,但它没有使用带有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$ 

看起来你需要使用常量引用。

答案 6 :(得分:-1)

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

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

在上面的例子中,如果没有error_code传递给函数,它将构造一个空的错误代码对象。

答案 7 :(得分:-1)

对于// 2你需要一个const引用

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

答案 8 :(得分:-2)

完全合法。

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