gpt4 book ai didi

c++ - 临时生命周期和完美的转发构造函数

转载 作者:可可西里 更新时间:2023-11-01 17:45:24 26 4
gpt4 key购买 nike

我无法理解为什么当周围有完美的转发构造函数时绑定(bind)到 const 引用参数的临时对象的生命周期会缩短。首先,我们了解绑定(bind)到引用参数的临时变量:它们持续到完整表达式:

A temporary bound to a reference parameter in a function call (5.2.2) persists until the completion of the full expression containing the call

但是我发现有些情况并非如此(或者我可能只是误解了完整表达式的含义)。让我们举一个简单的例子,首先我们定义一个对象,它有冗长的构造函数和析构函数:

struct A {
A(int &&) { cout << "create A" << endl; }
A(A&&) { cout << "move A" << endl; }
~A(){ cout << "kill A" << endl; }
};

还有一个对象包装器 B,它将用于引用折叠:

template <class T> struct B {
T value;
B() : value() { cout << "new B" << endl; }
B(const T &__a) : value(__a) { cout << "create B" << endl; }
B(const B &p) = default;
B(B && o) = default;
~B(){ cout << "kill B" << endl; };
};

我们现在可以使用我们的包装器来捕获对临时对象的引用并在函数调用中使用它们,如下所示:

void foo(B<const A&> a){ cout << "Using A" << endl; }
int main(){ foo( {123} ); }

上面的程序打印出我所期望的:

create A
create B
Using A
kill B
kill A

到目前为止一切顺利。现在让我们回到B并为可转换类型添加一个完美的转发构造函数:

template <class T> struct B {
/* ... */
template <class U, class = typename enable_if<is_convertible<U, T>::value>::type>
B(U &&v) : value(std::forward<U>(v)) {
cout << "new forward initialized B" << endl;
}
};

现在再次编译相同的代码会得到:

create A
new forward initialized B
kill A
Using A
kill B

注意我们的 A对象现在在使用之前就被杀死了,这很糟糕!为什么临时 not 的生命周期延长到 foo 的完整调用在这种情况下?此外,没有其他调用 A 的析构函数。 , 所以没有它的其他实例。

我可以看到两种可能的解释:

  • 两种类型都不是我认为的那样:将可转换移动构造函数更改为 B(T &&v)而不是 template <class U>B(U &&v)解决问题。
  • {123}不是 foo( {123} ) 的子表达式.交换 {123}对于 A(123)也解决了这个问题,这让我想知道大括号初始化器是否是完整表达式。

有人可以澄清这里发生了什么吗?

这是否意味着在某些情况下向类添加转发构造函数可能会破坏向后兼容性,就像它对 B 所做的那样?

您可以找到完整代码 here ,另一个测试用例因引用字符串而崩溃。

最佳答案

U 推断的类型在调用 B<A const&>::B(U&&)int ,因此唯一可以延长生命周期的临时调用 foomain是一个纯右值 int临时初始化为 123 .

成员(member)A const& value绑定(bind)到临时 A ,但是A在构造函数的 mem-initializer-list 中创建 B<A const&>::B(U&&)所以它的生命周期只在成员初始化期间延长[class.temporary]/5:

— A temporary bound to a reference member in a constructor’s ctor-initializer (12.6.2) persists until the constructor exits.

请注意,mem-initializer-listctor-initializer 中冒号之后的部分:

  template <class U, class = typename enable_if<is_convertible<U, T>::value>::type>
B(U &&v) : value(std::forward<U>(v)) {
^--- ctor-initializer
^--- reference member
^--- temporary A

这就是为什么 kill A new forward initialized B 之后打印.

Does this mean that adding a forwarding constructor to a class could break backward compatibility in some cases, like it did for B?

是的。在这种情况下,很难理解为什么需要转发构造函数;如果您有一个可以绑定(bind)临时对象的引用成员,那肯定是危险的。

关于c++ - 临时生命周期和完美的转发构造函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26484734/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com