指示返回值生命周期的 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++ 函数属性与参数相同的主要内容,如果未能解决你的问题,请参考以下文章

C++再识类和对象

c++中为啥要函数返回引用?

使用pybind11开发python扩展库

C语言中,哪种存储类的作用域与生命周期是不一致的?

C++重载重写重定义

c++类中 各种成员的生命周期?