下面的代码删除警告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
复制相似问题