【发布时间】:2013-01-24 13:24:30
【问题描述】:
我目前正在将我使用 Borland C++-Builder 5 和 6 开发多年的项目之一移植到最新的 Embarcadero C++-Builder XE 3 Update 2。XE 3 支持一些新的 C+ +11-诸如右值引用之类的东西,这对我来说当然是全新的,因为前者使用了非常旧的编译器。我只需要很少的修改就可以使我的项目可编译,但在运行时我面临一个问题,这似乎是新的右值引用和移动语义的结果。
我有一个类,其类型为 std::wstring 的字段存储一个路径,该路径只能从一个方法中读取,该方法在三元运算符中使用此字段,如下所示:
std::wstring retVal = someCondition ? this->classField : Util::doSomething(someArg);
someCondition 只是对 std::wstring.empty() 的调用,而 Util::doSomething 将 std::wstring 作为值返回,不涉及引用或其他内容,只是复制数据。
在 XE 3 中,第一次 someCondition 评估为 true 后,retVal 会正确填充 classField 的内容,但之后 classField 的内容为空。使用调试器,我可以跟踪执行到带有右值引用的优化赋值运算符:
#if _HAS_RVALUE_REFERENCES
[...]
_Myt& operator=(_Myt&& _Right)
{ // assign by moving _Right
return (assign(_STD forward<_Myt>(_Right)));
}
_Myt& assign(_Myt&& _Right)
{ // assign by moving _Right
[...]
我读到的关于右值引用的内容可以完美地解释我的问题,我什至发现了两个 cmets 解释了为什么我的 classField 被视为右值。
https://stackoverflow.com/a/8535301/2055163
https://stackoverflow.com/a/6957421
我可以通过另外一份 classField 的手动副本来修复上面的行:
std::wstring retVal = someCondition ? std::wstring(this->classField) : Util::doSomething(someArg);
但这并不能解决我需要在没有编译器帮助的情况下移植的每个项目中检查三元运算符(其中混合了左值和右值)的每一个用法的问题,因为它是一个运行时可能会发生也可能不会发生的问题。
我不明白的是,我对三元运算符的使用是否有错误或不好的做法?是否有更好的解决方案来检测这些病例?为什么我应该手动复制一些对象只是为了欺骗优化?这真的是预期的行为,并且在您的大多数代码库中都没有问题吗?您如何处理这些问题?
我现在对如何取得进展有点困惑,非常感谢任何建议和/或解释。谢谢!
我测试了以下两种情况:
第一个例子看起来和我的例子类似,期望没有使用类实例来存储全局字符串。 dummy2 已正确填充,并且 dummy 保留其内容。
std::wstring retVal = someCondition ? this->classField : doSomethingResult;
当我在使用三元运算符之前通过将 Util:... 的结果保存到 std::wstring 来更改上面的行以删除右值时,一切都按预期工作。 retVal 有它的内容,this->classField 也有它的内容。
现在的结论是什么? :-/
【问题讨论】:
-
可能是编译器错误。
Util::doSomething的返回类型是什么:是按值返回还是返回引用? -
我按值返回,根据我引用的链接,我看到的行为是我所期望的,我只是不明白为什么这是一件“好事”。我与三元运算符一起使用的两个表达式是左值和右值,导致整个表达式被用作右值,因此应用了移动语义。
-
你的编译器对这个程序做了什么? ideone.com/YwLjgi
-
是的,如果没有省略,您会在
main内部看到“复制 ctor / Move ctor / Destructor / Destructor”。但必须至少有一次复制 ctor 调用。 -
所以听起来你没有用更简单的代码重现问题。也许其他一些代码实际上意外地踩到了
this->classField?尝试减少完整程序和/或扩展 ideone 示例,直到找到答案或有 sscce.org
标签: c++ c++11 ternary-operator move-semantics rvalue-reference