2013-09-23 57 views
5
#include<iostream> 
using namespace std; 

int &fun() 
{ 
    static int x = 10; 
    return x; 
} 
int main() 
{ 
    fun() = 30; 
    cout << fun(); 
    return 0; 
} 

函数fun()通过引用返回值,但在main()方法中我将一些int赋值给函数。理想情况下,编译器应该显示一个像左值所需的错误,但在上述情况下,程序可以正常工作。为什么这样?为函数返回引用赋值

+5

返回参考静态变量是左值(和法律)。你为什么认为应该有任何错误? –

+0

'fun()'返回的值是'int&',它是'lvalue' –

+0

评估引用*会导致*左值。 –

回答

7

这是松散和马虎的语言说“一个函数返回的东西”。如果你知道如何处理这个问题,这可以作为简写,但在这种情况下,你会感到困惑。

更正确的方法来考虑它是你评估函数调用表达式。这样做会给你一个。值是一个右值或左值(模数细节)。

T是一个对象类型,并且您评估返回类型为T的函数时,您会得到一个类型为T的值,它是一个右值。另一方面,如果函数的返回类型为T &,则会得到类型为T的值,该值是一个左值(并且该值是与return语句中的引用绑定的值)。

+2

请注意,只有当f()计算为*内建类型*的右值时,'f()= value'才是错误的,否则如果它评估为类类型的右值则是正确的。 (左值在任何情况下都已经很好) – Nawaz

3

返回一个引用是非常有用的。

例如它是std::map::operator[]所做的。我希望你喜欢写my_map[key] = new_value;的可能性。

如果一个常规(非运算符)函数返回一个引用,那么它可以分配给它,我没有看到任何理由,这应该被禁止。

如果您真的想要,您可以通过返回const X&或返回X来防止分配。

2

它的工作原因是该功能的结果左值。参考文献是左值。基本上,从函数返回一个非const引用的全部就是能够赋值给它(或对引用对象执行其他修改)。

1

L值是一个定位器值。这意味着它有地址。一个参考清楚地有一个地址。该左值要求如果按值从乐趣()返回,你可以得到:

#include<iostream> 
using namespace std; 

int fun() 
{ 
    static int x = 10; 
    return x; 
} 
int main() 
{ 
    fun() = 30; 
    cout << fun(); 
    return 0; 
} 
2

除了其他的答案,考虑下面的代码:

SomeClass& func() { ... } 

func().memberFunctionOfSomeClass(value); 

这是一个非常自然的事如果你期望编译器给你一个这样的错误,我会很惊讶。

现在,当您编写some_obj = value;幕后真正发生的是您拨打some_obj.operator =(value);。而operator =()只是您班级的另一个成员功能,与memberFunctionOfSomeClass()没有区别。

所有的一切,把它归结为:

func() = value; 
// equivalent to 
func().operator =(value); 
// equivalent to 
func().memberFunctionOfSomeClass(value); 

当然这是过于简单,并且这个符号并不适用于内建类型,如int(但使用相同的机制)。

希望这会帮助你更好地理解其他人已经解释过的左值

1

我也被类似的代码所迷惑 - 在拳头。这是“为什么我给函数调用赋值,以及为什么编译器对它感到满意?”我质疑自己。但是当你看到“后面”会发生什么时,它确实有道理。


由于cpp和其他poined出来,左值是“存储位置”有地址,我们可以给它们赋值。您可以在左值和右值on the internet的主题中找到更多信息。

当我们在看功能:

int& fun() 
{ 
    static int x = 10; 
    return x; 
} 

我感动&的类型,所以这是比较明显的,我们正在返回的引用为int。
我们看到我们有x,这是左值 - 它有地址,我们可以分配给它。它也是静态的,这使得它非常特殊 - 如果它不是静态的,变量的生命期(范围)将在离开函数时以堆栈展开结束,然后参考可指向宇宙中存在的任何黑洞。然而,由于x是静态的,即使在我们离开函数之后(以及当我们再次返回函数时)它也会存在,我们可以在函数之外访问它。

我们正在返回一个int的引用,由于我们返回x,它是对x的引用。然后,我们可以使用该参考来更改函数外部的x。所以:

int main() 
{ 
    fun(); 

我们只是调用函数。变量x(在乐趣函数的范围内)被创建,它的值为10赋值。即使在功能被留下之后,它的地址和价值仍然存在 - 但我们无法使用它的价值,因为我们没有地址。

fun() = 30; 

我们调用的函数,然后改变X值。通过函数返回的引用更改值x注意:该函数被称为第一个,并且只有在函数调用完成后,然后,分配发生。

int& reference_to_x = fun(); // note the & 

现在我们(终于)保持由该函数返回的参考X。现在我们可以改变x而不先调用函数。(reference_to_x将可能有相同的地址X有乐趣函数内)

int copy_of_x = fun(); // no & this time 

这一次,我们创造新的int和我们刚才复制的X值(通过参考)。这个新的int有它自己的地址,它不指向x like reference_to_x是。

reference_to_x = 5; 

我们分配X值5至基准,而我们甚至没有调用的函数。 copy_of_x未更改。

copy_of_x = 15; 

我们换了新的int价值15 X没有改变,因为copy_of_x有它自己的地址。

} 


由于6502和其他人指出的那样,我们使用返回引用了大量与容器和自定义的替换类似的方法。

std::map<std::string, std::string> map = {}; 

map["hello"] = "Ahoj"; 
// is equal to 
map.operator[]("hello") = "Ahoj"; // returns reference to std::string 
// could be done also this way 
std::string& reference_to_string_in_map = map.operator[]("hello"); 
reference_to_string_in_map = "Ahoj"; 

我们使用可以有声明这样的地图功能:

std::string& map::operator[](const std::string& key); // returns reference 

我们没有地址,我们“存储”在地图的字符串,所以我们称之为地图此重写功能,传递它以便map知道我们想要访问哪个字符串,并且它返回给我们的那个字符串的引用,我们可以用它来改变这个值。 注意:该函数再次被调用,并且只有在完成后(地图找到正确的字符串并返回对其的引用)才会进行分配。这就像有乐趣()= 10,只有更beatiful ...

希望这有助于人谁仍然woudn't甚至阅读其他的答案后明白了一切......