会不会有一个临时的被销毁的返回值?
Will there be a temporary for the returned value that gets destroyed?
我对复制省略的新规则有点困惑,实际上我什至不确定它是否适用于这种情况。我有这个:
template <typename T> struct foo {
T t;
foo(const T& t) : t(t) {}
~foo() { std::cout << "destructor \n"; }
}
template <typename T> foo<T> make_foo(const T& t) { return {t}; }
其中 make_foo
仅允许推导参数(在实际代码中 t
是一个 lambda,但为了简单起见,或者更确切地说,为了混乱,对此感到抱歉)如
auto x = make_foo(123);
现在我需要绝对确定 foo
的析构函数只被调用一次:当 x
超出范围时。恐怕这是一个不清楚你在问什么的问题,但如果很明显不会有任何临时 foo
,那就足够回答了 ;)。
在 C++11 中,我可以确定 make_foo
中不会有临时的 foo
会被销毁吗?仅当 x
超出范围时才应调用析构函数。
正如评论中正确指出的那样,这个问题是 XY 问题的 Y 部分,而 X 部分是我想实现一些范围结束功能。 foo
的析构函数有一些副作用(在示例中是 cout
),应该在 x
的范围末尾调用,而不是在 make_foo
中调用,以防万一是一些临时的 foo
.
自 C++17 起保证没有临时的。
在 C++14 及更早版本中,必须有一个可访问的 copy/move 构造函数,并且编译器是否实际存在临时构造函数是可选的。
据我所知,唯一会实际显示临时文件的编译器是调试模式下的旧版本 MSVC。
在 C++11 中,即使是无名的临时复制省略也只是允许而不是强制的。它在此处描述 copy elision。自 C++17 起强制执行。
同样在 C++17 中,您将拥有自动推导指南,因此您将不需要这样的构造。
并且您可以测试您的编译器,因为大多数现代编译器都会在此处省略复制。
在您的情况下,要确保不会为未命名对象调用析构函数,您可以将 return 值绑定到 const 引用 。
澄清如果我们不依赖复制省略会发生什么:
template <typename T> foo<T> make_foo(const T& t) { return {t}; }
在此函数中,return 对象将不会在该函数的范围内构造。它将在范围外创建 临时未命名对象。
如果您将 return 值绑定到 新命名对象 将调用移动构造函数(如果未定义移动则复制)以创建您 新对象 来自 return 临时 。但是,如果您将 returned temporary 绑定到 const reference 它将严格绑定到该引用,并且不会构造新对象并且在该引用结束之前不会破坏 temporary的范围。
编辑:
为了不误导你。在函数范围内调用的临时构造函数,但该临时函数的生命周期确实会延长到 const 引用的生命周期
如果您需要更多信息,可以查看此答案。它指的是C++标准。
Does a const reference prolong the life of a temporary?
我对复制省略的新规则有点困惑,实际上我什至不确定它是否适用于这种情况。我有这个:
template <typename T> struct foo {
T t;
foo(const T& t) : t(t) {}
~foo() { std::cout << "destructor \n"; }
}
template <typename T> foo<T> make_foo(const T& t) { return {t}; }
其中 make_foo
仅允许推导参数(在实际代码中 t
是一个 lambda,但为了简单起见,或者更确切地说,为了混乱,对此感到抱歉)如
auto x = make_foo(123);
现在我需要绝对确定 foo
的析构函数只被调用一次:当 x
超出范围时。恐怕这是一个不清楚你在问什么的问题,但如果很明显不会有任何临时 foo
,那就足够回答了 ;)。
在 C++11 中,我可以确定 make_foo
中不会有临时的 foo
会被销毁吗?仅当 x
超出范围时才应调用析构函数。
正如评论中正确指出的那样,这个问题是 XY 问题的 Y 部分,而 X 部分是我想实现一些范围结束功能。 foo
的析构函数有一些副作用(在示例中是 cout
),应该在 x
的范围末尾调用,而不是在 make_foo
中调用,以防万一是一些临时的 foo
.
自 C++17 起保证没有临时的。
在 C++14 及更早版本中,必须有一个可访问的 copy/move 构造函数,并且编译器是否实际存在临时构造函数是可选的。
据我所知,唯一会实际显示临时文件的编译器是调试模式下的旧版本 MSVC。
在 C++11 中,即使是无名的临时复制省略也只是允许而不是强制的。它在此处描述 copy elision。自 C++17 起强制执行。
同样在 C++17 中,您将拥有自动推导指南,因此您将不需要这样的构造。
并且您可以测试您的编译器,因为大多数现代编译器都会在此处省略复制。
在您的情况下,要确保不会为未命名对象调用析构函数,您可以将 return 值绑定到 const 引用 。 澄清如果我们不依赖复制省略会发生什么:
template <typename T> foo<T> make_foo(const T& t) { return {t}; }
在此函数中,return 对象将不会在该函数的范围内构造。它将在范围外创建 临时未命名对象。 如果您将 return 值绑定到 新命名对象 将调用移动构造函数(如果未定义移动则复制)以创建您 新对象 来自 return 临时 。但是,如果您将 returned temporary 绑定到 const reference 它将严格绑定到该引用,并且不会构造新对象并且在该引用结束之前不会破坏 temporary的范围。
编辑: 为了不误导你。在函数范围内调用的临时构造函数,但该临时函数的生命周期确实会延长到 const 引用的生命周期
如果您需要更多信息,可以查看此答案。它指的是C++标准。
Does a const reference prolong the life of a temporary?