- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
我无法理解为什么当周围有完美的转发构造函数时绑定(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
,因此唯一可以延长生命周期的临时调用 foo
在 main
是一个纯右值 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-list 是 ctor-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/
我正在开发一个使用多个 turtle 的滚动游戏。玩家 turtle 根据按键命令在 Y 轴上移动。当危害和好处在 X 轴上移动时,然后循环并改变 Y 轴位置。我尝试定义一个名为 colliding(
我不明白为什么他们不接受这个作为解决方案,他们说这是一个错误的答案:- #include int main(void) { int val=0; printf("Input:- \n
我正在使用基于表单的身份验证。 我有一个注销链接,如下所示: 以及对应的注销方法: public String logout() { FacesContext.getCurren
在 IIS7 应用程序池中有一个设置 Idle-time out 默认是 20 分钟,其中说: Amount of time(in minutes) a worker process will rem
我是一名优秀的程序员,十分优秀!