英俊的针织衫 · 国产芯片对抗英伟达的“唯一希望”?六百亿独角 ...· 1 周前 · |
潇洒的伤疤 · 【原创】真·修复npm ERR! cb() ...· 10 月前 · |
刚毅的灭火器 · 一季度纯电销量同比增长89%,奔驰欧美纯电市 ...· 1 年前 · |
强悍的双杠 · 手卷烟丝类别介绍 - 知乎· 1 年前 · |
发财的李子 · 005 他背后有人?-都市极品医神-漫画牛· 1 年前 · |
下面的代码删除警告C4172:返回本地变量或临时地址。但我想知道在这种情况下这是一个真正的错误吗?我知道这里有很多类似的话题,我也从这个警告中读到了很多类似的话题。因此,在这种情况下,返回的值是来自"main“函数的指针,它应该是活动的,直到程序结束。如果returningLocalPointer将返回:"A某事;返回&某样东西;“那么是的,这将是一个问题,但在这种情况下,我们返回一个指针,该指针一直存在到"main”结尾。还是我错了?
class A
A* returningLocalPointer(A* a)
return a;
template<typename T>
T const& doWarning(T const& b)
A* c = returningLocalPointer(b);
return c; // error if uses call-by-value
int main()
auto m = doWarning(&d); //run-time ERROR
}
发布于 2021-12-27 09:58:01
是的,这是个真正的问题。您的程序的行为没有定义。
c
是一个与
b
引用的指针不同的对象,它的生存期在
doWarning
的末尾结束。这两个指针指向同一个
A
对象(
d
),但这并不意味着它们是同一个对象。
为了举例说明,我将或多或少地逐行使用图表:
A d;
auto m = doWarning(&d);
这将创建一个名为
A
的
d
对象,并将指向该对象的匿名指针传递给
doWarning
。稍后我将讨论
m
,但目前正在运行的对象如下所示:
d
┌─────┐ ┌─────┐
│ │ │ │
│ A* ├──────►│ A │
│ │ │ │
└─────┘ └─────┘
template<typename T>
T const& doWarning(T const& b)
{
在这里,
T
将被推断为
A*
,因为这就是传递给它的内容。
doWarning
通过引用接受它的参数,所以
b
的类型将是
A* const &
。也就是说,
b
是对来自
main
的
d
的匿名指针的引用。
b d
┌───────────┐ ┌─────┐ ┌─────┐
│ │ │ │ │ │
│ A* const& ├──────►│ A* ├──────►│ A │
│ │ │ │ │ │
└───────────┘ └─────┘ └─────┘
A* c = returningLocalPointer(b);
在这里,您创建了另一个指针
c
,它指向与
b
相同的对象。我不会看
returningLocalPointer
,因为它或多或少是不相关的。这一行可以用
A* c = b;
代替,不会有任何改变。您的对象现在看起来如下:
b d
┌───────────┐ ┌─────┐ ┌─────┐
│ │ │ │ │ │
│ A* const& ├──────►│ A* ├──────►│ A │
│ │ │ │ │ │
└───────────┘ └─────┘ └─────┘
c │
┌─────┐ │
│ │ │
│ A* ├──────────┘
│ │
└─────┘
如您所见,
c
是一个与
b
引用的对象不同的对象。
return c;
因为
doWarning
返回一个
A* const&
(因为
T
是
A*
),所以初始化返回值以引用局部变量
c
b d
┌───────────┐ ┌─────┐ ┌─────┐
│ │ │ │ │ │
│ A* const& ├──────►│ A* ├──────►│ A │
│ │ │ │ │ │
└───────────┘ └─────┘ └─────┘
return value c │
┌───────────┐ ┌─────┐ │
│ │ │ │ │
│ A* const& ├──────►│ A* ├──────────┘
│ │ │ │
└───────────┘ └─────┘
}
现在
doWarning
结束了,因此它的局部变量
c
超出了作用域,它的生存期结束了。这使得
doWarning
的返回值悬空:
b d
┌───────────┐ ┌─────┐ ┌─────┐
│ │ │ │ │ │
│ A* const& ├──────►│ A* ├──────►│ A │
│ │ │ │ │ │
└───────────┘ └─────┘ └─────┘
return value
┌───────────┐
│ │
│ A* const& ├──────► Nothing here anymore
│ │
└───────────┘
auto m = doWarning(&d);
现在我们回到
m
。
auto
本身永远不会推导出引用类型,因此
m
的类型被推断为
A*
。这意味着程序将尝试复制
doWarning
返回的引用所引用的指针。但是,
doWarning
的返回值引用的指针已经不存在了。尝试复制一个不存在的对象是一个错误,如果程序这样做,它的行为是未定义的。
发布于 2021-12-27 09:33:58
让我们用
T = A*
“实例化”这个函数(就像用
doWarning(&d)
调用它时一样):
template
A* const& doWarning<A*>(A* const& b)
A* c = returningLocalPointer(&b);
return c;
}
你也许能看到问题出在哪里。
c
是通过引用返回的,但是它是一个局部变量,立即销毁,因此
doWarning
总是返回一个悬空引用。
MSVC似乎对指向本地的指针和对本地的引用使用相同的警告,这就是为什么它在谈论地址时实际上是关于引用的。GCC的警告可能会更加明确:
In instantiation of 'const T& doWarning(const T&) [with T = A*]':
warning: reference to local variable 'c' returned [-Wreturn-local-addr]
return c; // error if uses call-by-value
note: declared here
A* c = returningLocalPointer(b);
^
发布于 2021-12-27 08:53:32
您将返回对局部变量
c
的引用,因此您的代码具有未定义的行为。您可能会得到“幸运”,
m
将碰巧成为指向
d
的指针,但这并不能保证。
当它返回对
d
的引用而不是对
c
的引用时,该代码将定义行为,尽管它仍然有未定义的行为(因此可能仍然会产生警告),如果使用临时值调用
doWarning
:
A* returningLocalPointer(A* a)
return a;
template<typename T>
T const& doWarning(T const& b)
A* c = returningLocalPointer(&b);
return *c;
int main()