C++ 为什么这会导致C2102:'&';需要l值

C++ 为什么这会导致C2102:'&';需要l值,c++,C++,我想知道,为什么下面的代码方式(已经注释掉)会导致 C2102:“&”需要l值 有没有更好的方法避免使用tmp变量 class a { private: int *dummy; public: int* get_dummy() const { return dummy; } }; int main() { a aa; // error C2102: '&' requires l-value //int** me = &a

我想知道,为什么下面的代码方式(已经注释掉)会导致
C2102:“&”需要l值

有没有更好的方法避免使用
tmp
变量

class a {
private:
    int *dummy;
public:
    int* get_dummy() const {
        return dummy;
    }
};

int main()
{
    a aa;

    // error C2102: '&' requires l-value
    //int** me = &(aa.get_dummy());

    // OK!
    int *tmp = aa.get_dummy();
    int** me = &(tmp);
}
没有

否则,
me
会包含什么地址?这里您给了它
tmp
——但是如果您将它替换为
int**me=&aa.get_dummy(),它指向哪里


这个问题没有有意义的答案,因此标准要求
&
的参数为左值。

您可以定义:

int **get_dummy() ... return &dummy;
从本质上讲,可以将r值视为表达式,而l值是实际对象。表达式没有地址,即使有,也很难想象地址会有多好。很容易理解对象的地址是如何有用的


抽象地理解这样一个问题有点困难。理解指针和编译语言的最好方法是学习汇编语言。

必须对左值应用
&
运算符。当调用
aa.get_dummy()
未分配给变量时,其返回值仅放在堆栈上,因此获取堆栈项的地址是愚蠢的(也是错误的)。

因为
a::get_dummy()
返回一个未命名的临时对象(int指针)

函数返回的对象<强> >栈帧,它的地址毫无意义,因为表达式结束后可能是强>无效。< /强> < /p> < p>编译器是正确的,按照ISOC++:

一元运算符&的结果是指向其操作数的指针。这个 操作数应为左值或限定id

换句话说,你可以把任何有名字的东西的地址记下来

按值从函数返回的值没有名称,通常通过寄存器返回。因此,没有“地址”可言,因为值不在内存中

有人可能会争辩说,编译器可以更智能,检测到这一点,并在使用地址的表达式期间将值存储在堆栈上。但这很容易出错(您可以“泄漏”指向表达式外部的指针),并且显然是标准的扩展(即不保证兼容)。所以MSVC干脆禁止它

有趣的是,编译器在引用右值时。但是对于指向右值的指针没有这样的功能

回答你的问题:尽量减少获取物品地址;获取变量的地址可防止优化器将其放入寄存器。 但如果必须,请返回一个引用:

class a {
private:
    int dummy;
public:
    int get_dummy() const {
        return dummy;
    }
    int& get_dummy() {
        return dummy;
    }
};

int main()
{
    a aa;

    int* me = &(aa.get_dummy());
}

请注意,严格来说,不需要一个
常量get_dummy()
,但它会在右值上下文中帮助优化器。

它会指向哪里?
-它不是指向虚拟实例的地址吗?@Yan:否。该函数返回
dummy
存储的地址(或值),不是
dummy
本身。@Yan:您可以通过引用返回dummy,例如int*&get\u dummy.Hmm,您可能可以使用
&aa
,但是,否则,为什么要使用间接指针?是否没有API来更改指针?你想改变它吗?获取局部变量的地址是危险的……无论是全局变量、成员变量还是局部变量,只要其生存期超过引用,指针悬空的“危险”就不存在。@YeenFei,当然,但返回局部变量的地址在任何语言中都是一种奇特的操作。当人们不完全理解运行时的现实时,保持更明显的安全可能是一个好主意。与Luca的答案相同
tmp
是一个堆栈对象
dummy
不是临时对象或本地对象。@Naveen:是的,这是真的。但答案并不是说“未命名的临时对象”(在其完整表达式结束前有效),而是说“堆栈对象”,这通常意味着“自动”存储中的对象在其作用域结束前有效。@Billy,在我的答案中添加了“未命名的对象”。但我在我的回复中并没有看到你们所指的“堆栈对象”。@YeenFei:stack对象由“堆栈框架顶部”和“超出范围”表示<代码>tmp
超出范围时也无效。未命名的编译器临时变量与作用域完全无关。它们一直生存到其所拥有的完整表达式的结尾。返回指针的引用。的可能重复