我目前正在将我使用 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
[...]
我读到的关于右值引用的内容可以完美地解释我的问题,我什至发现两条评论解释了为什么我的 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 也有它的内容。
现在的结论是什么?:-/