在不复制临时对象的情况下延长其生存期
Extending the lifetime of a temporary object without copying it
请考虑以下代码:
#include <utility>
#include <iostream>
struct object {
object(const object&) = delete;
object(object&&) = delete;
object() {std::clog << "object::object()n";}
~object() {std::clog << "object::~object()n";}
void operator()() const {std::clog << "object::operator()()n";}
};
struct wrapper {
const object& reference;
void operator()() const {reference();}
};
template <class Arg>
wrapper function(Arg&& arg) {
wrapper wrap{std::forward<Arg>(arg)};
return wrap;
}
int main(int argc, char* argv[]) {
wrapper wrap = function(object{}); // Let's call that temporary object x
wrap();
return 0;
}
我真的很惊讶它打印
:object::object()
object::~object()
object::operator()()
问题 1:为什么对象x
的生存期不会超过函数调用,即使 const 引用已绑定到它?
问题 2:有没有办法实现wrapper
,以便它将x
的生存期延长到函数调用之后?
注意:object
的复制和移动构造函数已被显式删除,以确保它只存在一个实例。
为什么对象 x 的生存期不会超过函数调用,即使 const 引用已绑定到它?
从技术上讲,对象的生存期延长到函数调用之后。然而,它并没有扩展到wrap
初始化之后。但这是一个技术问题。
在我们深入研究之前,我将进行简化:让我们摆脱wrapper
。另外,我正在删除模板部分,因为它也无关紧要:
const object &function(const object &arg)
{
return arg;
}
这完全不会改变代码的有效性。
鉴于此声明:
const object &obj = function(object{}); // Let's call that temporary object x
你想要的是编译器识别"对象x
"和obj
引用同一个对象,因此应该延长临时对象的生存期。
那是不可能的。编译器不能保证有足够的信息来了解这一点。为什么?因为编译器可能只知道这一点:
const object &function(const object &arg);
看,function
的定义将arg
与返回值相关联。如果编译器没有function
的定义,那么它就无法知道传入的对象是返回的引用。没有这些知识,它就无法延长x
的寿命。
现在,你可能会说,如果提供了function
的定义,那么编译器就可以知道。嗯,有复杂的逻辑链可能会阻止编译器在编译时知道。您可以这样做:
const object *minimum(const object &lhs, const object &rhs)
{
return lhs < rhs ? lhs : rhs;
}
好吧,这将返回对其中一个的引用,但哪个将仅根据对象的运行时值来确定。呼叫者应延长谁的寿命?
我们也不希望代码的行为根据编译器是只有声明还是具有完整定义而更改。如果代码只有一个声明,则编译代码总是可以的,或者只使用声明编译代码永远不行(如inline
、constexpr
或template
函数的情况(。声明可能会影响性能,但不会影响行为。这很好。
由于编译器可能没有识别参数const&
超过函数生存期所需的信息,即使它具有该信息,也可能不是可以静态确定的东西,因此C++标准甚至不允许实现尝试解决问题。因此,每个C++用户都必须认识到,如果临时函数返回引用,则调用函数可能会导致问题。即使引用隐藏在某个其他对象中。
你想要的做不到。这就是为什么您不应该使对象完全不可移动的原因之一,除非它对其行为或性能至关重要。
据我所知,如果对于函数的返回值,则生存期延长的唯一情况,
struct A { int a; };
A f() { A a { 42 }; return a`}
{
const A &r = f(); // take a reference to a object returned by value
...
// life or r extended to the end of this scope
}
在代码中,将引用传递给 A 类的"构造函数"。因此,您有责任确保传递的对象存活更长时间。因此,上面的代码包含未定义的行为。
您看到的可能是不引用任何成员的类中最可能的行为。如果要访问对象成员(包括向量表(,则很可能会观察到冲突访问。
话虽如此,在您的情况下,正确的代码是:
int main(int argc, char* argv[])
{
object obj {};
wrapper wrap = function(obj);
wrap();
return 0;
}
也许你想要的是将临时对象移动到包装器中:
struct wrapper {
wrapper(object &&o) : obj(std::move(o)) {}
object obj;
void operator()() const {obj();}
};
无论如何,原始代码没有多大意义,因为它是围绕错误的假设构建的,并且包含未定义的行为。
临时对象的生存期实质上是创建它的表达式的结束。也就是说,当处理wrap = function(object{})
完成时。
所以在简历中:
答案 1因为您尝试将生命周期延长应用于标准中指定的上下文以外的上下文。
答案 2就像将临时对象移动到永久对象一样简单。
- 在没有太多条件句的情况下,我如何避免被零除
- 为什么在没有显式默认构造函数的情况下,将另一个结构封装在联合中作为成员的结构不能编译
- 在未初始化映射的情况下,将值插入到映射的映射中
- 是默认情况下分配给char数组常量的值
- 为什么我不能在不创建字符串变量的情况下使用函数的字符串输出
- 如何在不产生任何垃圾的情况下获得C中的像素
- 在已经使用Git的情况下减少编译时间
- 为什么在Windows上的VS 2019和Clang 9中"size_t"在没有标题的情况下工作
- GCC对可能有效的代码抛出init list生存期警告
- 如何在没有信号的情况下从C++执行QML插槽
- 如何在不知道向量大小的情况下输入向量内部的向量?
- 为什么在某些情况下不写入此文件?
- 为什么Mat类的两个对象可以在不重载运算符+的情况下添加
- 在没有Xcode的情况下在Mac捆绑包中嵌入框架
- UE4-如何在给定4个屏幕坐标的情况下缩放纹理或材质
- 在不复制临时对象的情况下延长其生存期
- 是否存在对象存储在其生存期内可能会更改的情况?
- 对象生存期,在这种情况下重用存储
- 如何在没有嵌套作业对象的情况下可靠地控制子进程生存期
- 在不调用析构函数的情况下结束STL容器的生存期