这段代码有未定义的行为:
#include <string>
std::string make_str(const char* s)
{
return s;
}
const char* get_str(const std::string& s)
{
return s.c_str();
}
const char* bad()
{
return get_str(make_str("hello"));
}
错误的函数创建了一个临时的 std::string 并返回一个指向其数据的指针,该函数一返回就无效。
GCC 5+ 捕获此(“函数返回局部变量的地址”)但仅当使用 -O3
编译时。在包括 -O2
在内的更典型的优化级别,GCC 可以毫无怨言地编译它,即使使用 -Wall -Wextra
。除非您使用实验性 -Wlifetime
功能,否则 Clang 永远不会捕获它。
我的问题是:我们能否明确地告诉编译器这种生命周期依赖性,例如使用属性?例如,我希望能够这样做:
[[lifetime-depends : s]] // hypothetical syntax
const char* get_str(const std::string& s);
或者可能是这样:
const char* get_str(const std::string& s)
__attribute__((lifetime-depends(0))); // hypothetical syntax
我会接受适用于任何官方版本的 GCC 或 Clang 的答案,但更喜欢带有 C++14 的 GCC 6.1。 Clang 的实验性 -Wlifetime
不是答案,因为我想要明确,而不是依赖启发式方法(这似乎无论如何都不适用于多个翻译单元)。
或者,我会接受一个答案,说明为什么这没有用或无法实现。
最佳答案
基于意见的答案,不提供答案,但质疑问题背后的想法(并且评论的文字太多):
恕我直言,这意味着编译器智能在捕获用户错误方面具有相当大的可靠性,因为那是您发布的代码,失败了。该标准已经涵盖了如何处理临时对象的生命周期,并考虑在引用仍在使用的情况下延长生命周期(参见草案 n4713,例如 15.2 Bullet 5)。但是,您发布的示例是不同的情况...
同时,论文 p1179r0 (P.W 的评论)确实提供了指向存储在栈上 对象的指针的想法,它对堆上的对象没有帮助。又该如何呢?返回指向 char(或任何类型)的指针不是错误...返回指向不再存在的数组的指针是。它怎么知道 c_str() 返回一个指向一个临时数组的指针,这个数组在它的析构函数中删除了(有点具体,不是吗?)?如何知道数组是否被销毁?如果删除完成,通过查看析构函数?如果析构函数不删除怎么办?关于析构函数应该删除的警告?如果 new
是新展示位置怎么办?如果……工厂模式……怎么办?如果为智能指针(例如:shared_ptr)创建指针怎么办...?如果再增加 200 分会怎样……在堆上管理生命周期,主要会导致垃圾收集。
你得到的警告是 function returns address of local variable [-Wreturn-local-addr]
我认为这与你的代码无关,但更多的是“意外”造成的通过短字符串优化。 SSO 优化的 std::string
does 如消息所述返回指向本地的指针...简单测试,使您的字符串长于 16 个字符,警告 disappears ...
关于C++ 函数属性指示返回值的生命周期与参数相同,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54570121/