指示返回值生命周期的 C++ 函数属性与参数相同
Posted
技术标签:
【中文标题】指示返回值生命周期的 C++ 函数属性与参数相同【英文标题】:C++ function attribute to indicate lifetime of return value is same as argument 【发布时间】:2019-02-07 09:29:39 【问题描述】:此代码具有未定义的行为:
#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"));
bad 函数创建一个临时的 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
不是答案,因为我想要明确,而不是依赖启发式(无论如何这似乎不适用于多个翻译单元)。
或者,我会接受一个回答,说明为什么这不会有用或无法实施。
【问题讨论】:
herb sutter 在 cppcon 2019 上谈到了这一点。它现在并不能真正帮助您,但我相信它可能会在 c++20 或更高版本中出现。 基本上是-Wlifetime
。见***.com/questions/52662135/…
谈话的印刷版我相信是这样的:open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1179r0.pdf
只要不要在任何地方返回原始指针,问题就解决了。
@MatthieuBrucher: -Wlifetime
过分热心,它声称get_str()
会返回一个悬空引用,即使它执行return "";
并完全忽略该参数。换句话说,它只是假设存在依赖关系——即使函数是微不足道的和内联的!鉴于在编译现有第三方代码时可能会出现这种误报,我想要一个明确的属性,而不是带有误报的启发式。见godbolt.org/z/7eRDSI
【参考方案1】:
基于意见的答案,不提供答案,但质疑问题背后的想法(并且评论文字过多):
恕我直言,这意味着编译器智能在捕获用户错误方面非常可靠,因为那是您发布的代码,失败了。该标准已经涵盖了如何处理临时对象的生命周期,并且确实考虑在引用仍在使用的情况下延长生命周期(参见草案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
确实 如消息所述返回指向本地的指针...简单测试,使您的字符串长度超过 16 个字符,警告 disappears...
【讨论】:
顺便说一句,SSO 使string
成为非标准实践容器。容器内部没有元素。在通常的实践中,交换不会使迭代器失效。以上是关于指示返回值生命周期的 C++ 函数属性与参数相同的主要内容,如果未能解决你的问题,请参考以下文章