临时函数参数的生存期
lifetime of a temporary function parameter
创建一个临时的char缓冲区作为默认函数参数并将r值引用绑定到它允许我们在一行上编写语句,同时避免需要在堆上创建存储。
const char* foo(int id, tmp_buf&& buf = tmp_buf()) // buf exists at call-site
将引用/指针绑定到临时缓冲区并在以后访问它会产生未定义的行为,因为临时不再存在。
从下面的示例应用中可以看出,tmp_buf
析构函数是在第一个输出之后和第二个输出之前调用的。
我的编译器(gcc-4.8.2
)没有警告我正在将变量绑定到临时变量。这意味着使用这种微优化来使用自动字符缓冲区而不是std::string
关联的堆分配是非常危险的。
其他人进来并捕获返回的const char*
可能会无意中引入错误。
1. 有没有办法让编译器警告下面的第二种情况(捕获临时)?
有趣的是,您可以看到我试图使缓冲区无效 - 但我没有这样做,所以这可能表明我不完全了解堆栈上tmp_buf
正在创建的位置。
2.为什么我打电话给try_stomp()
时没有把tmp_buf
的记忆扔掉?如何tmp_buf
垃圾?
3. 或者 - 以我展示的方式使用是否安全?(我没想到这是真的!
法典:
#include <iostream>
struct tmp_buf
{
char arr[24];
~tmp_buf() { std::cout << " [~] "; }
};
const char* foo(int id, tmp_buf&& buf = tmp_buf())
{
sprintf(buf.arr, "foo(%X)", id);
return buf.arr;
}
void try_stomp()
{
double d = 22./7.;
char buf[32];
snprintf(buf, sizeof(buf), "pi=%lf", d);
std::cout << "n" << buf << "n";
}
int main()
{
std::cout << "at call site: " << foo(123456789);
std::cout << "n";
std::cout << "after call site: ";
const char* p = foo(123456789);
try_stomp();
std::cout << p << "n";
return 0;
}
输出:
at call site: foo(75BCD15) [~]
after call site: [~]
pi=3.142857
foo(75BCD15)
对于问题 2。
您没有删除变量的原因是编译可能在函数调用开始时分配了所需的所有堆栈空间。这包括临时对象的所有堆栈空间,以及在嵌套作用域内声明的对象。你不能保证编译器这样做(我认为),而不是根据需要在堆栈上推送对象,但以这种方式跟踪堆栈变量的位置更有效、更容易。
当您调用 try_stomp
函数时,该函数会在 main
函数的堆栈之后(或之前,具体取决于您的系统)分配其堆栈。
的默认变量实际上是通过编译到调用代码,而不是被调用函数的一部分(这就是为什么需要成为函数声明的一部分,而不是定义,如果它是单独声明的)。
所以你的堆栈在try_stomp
看起来像这样(堆栈中还有很多事情要做,但这些是相关的部分):
main - p
main - temp1
main - temp2
try_stomp - d
try_stomp - buf
所以你不能从try_stomp
中丢弃临时的,至少不能不做一些非常离谱的事情。
同样,您不能依赖此布局,因为它依赖于编译,并且只是编译器如何执行此操作的示例。
丢弃临时缓冲区的方法是在 tmp_buf
的析构函数中执行此操作。
同样有趣的是,MSVC 似乎单独为所有临时对象分配堆栈空间,而不是为两个对象重复使用堆栈空间。这意味着即使重复调用foo
也不会相互破坏。同样,你不能依赖这种行为(我认为 - 我找不到对它的引用)。
关于问题3。不,不要这样做!
- GCC对可能有效的代码抛出init list生存期警告
- 在不复制临时对象的情况下延长其生存期
- 结束另一个线程中使用的对象的生存期
- "this"指针的值在对象的生存期内是否恒定?
- 创建具有全局生存期的 UObject
- C++17 和静态临时生存期的参考扩展
- 用于指示返回值生存期的C++函数属性和参数相同
- async_resolve和async_connect参数的生存期
- 作为参数立即传递的对象生存期是多少
- 临时绑定到引用参数的默认参数的生存期是多少
- 对参数生存期的约束 std::regex_match(和 std::regex_search)
- 使函数内联是否会影响绑定到其参数的临时变量的生存期
- std::thread 参数的生存期
- std::字符串的生存期作为参数传递
- 绑定到函数参数的引用是否会延长该临时参数的生存期
- 临时函数参数的生存期
- 提升 Python:使用 return_internal_reference 将参数的生存期与返回值绑定
- c++ WIN API:当使用CreateProcess创建子进程时,我需要使输入参数具有全局生存期吗?
- 异步函数调用的参数生存期
- 在参数列表中为使用*non*const引用参数的函数创建的临时的生存期是否包含该函数调用