下面的代码删除警告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()
{
A d;
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()
{
A d;
auto& m = doWarning(d);
// Undefined behaviour
auto& n = doWarning(A{});
}https://stackoverflow.com/questions/70493226
复制相似问题