我有一个我无法修改的外部库。该库声明了一个模板函数,该函数由于某种原因返回const
非引用对象:
template<class C>
const C foo();
我有另一个我无法修改的外部库。该库声明了一个不可复制的类,并且只有来自非const对象的移动构造函数:
struct bar {
bar();
bar(const bar&)=delete;
bar(bar&&);
};
现在我需要使用foo<bar>
。一个简单的用法:
bar buz() {
return foo<bar>();
}
失败
main.cpp: In function 'bar buz()': main.cpp:13:21: error: use of deleted function 'bar::bar(const bar&)' return foo<bar>(); ^ main.cpp:8:5: note: declared here bar(const bar&)=delete; ^~~
这是有道理的,没有简单的解决方法使代码编译。
但是,如果我添加一些更复杂的解决方法:
bar buz() {
return const_cast<bar&&>(std::move(foo<bar>()));
}
它编译并且整个代码按预期工作(不仅是上面的简化示例,而且我的真实代码也是如此)。
但是,它是安全的,还是我遇到了一些未定义的行为?有没有更好的解决方法?
我已阅读并理解有关从函数(1,2)返回const
的问题,并且常见的答案似乎是返回const
个对象是在现代C ++中不鼓励,但我的问题不是关于它,而是关于当外部库返回const
对象时如何解决这种情况。
答案 0 :(得分:7)
如果bar
的移动构造函数修改了任何内容,则抛弃const将导致未定义的行为。你可以解决这个问题,而不会引入未定义的行为:
struct wrapped_bar {
mutable bar wrapped;
};
bar buz()
{
return foo<wrapped_bar>().wrapped;
}
wrapped
成员是可变的意味着该成员是非const的,即使wrapped_bar
对象作为一个整体是const。根据{{1}}的工作原理,您可能需要将成员添加到foo()
,以使其更像wrapped_bar
。
答案 1 :(得分:4)
从技术上讲,您将程序暴露给未定义的行为。由于原始对象C
(临时)被声明为const
,因此const-casting和修改它是违法的并且违反标准。 (我假设,移动构造函数对movee做了一些修改)。
话虽如此,它可能适用于您的环境,但我没有看到更好的解决方法。
答案 2 :(得分:1)
由于函数调用的结果是一个R-Value本身,你不需要在return语句中对它应用std::move
- const_cast<bar&&>(foo<bar>())
就足够了。这使代码更容易阅读。
仍然 - 没有标准保证这将始终适用于所有bar
类型。甚至更多 - 这可能在某些情况下会导致未定义的行为。(想象一下非常具有侵入性的优化,它完全消除foo
并使其结果成为静态数据&#34;内存段的对象 - 如果foo
是constexpr
。然后调用移动构造函数(可能修改其参数)可能会导致访问冲突异常)。
您所能做的就是切换到不同的库(或者,如果可能的话,请求库维护人员修复API)或者创建一些单元测试并将其包含在您的构建过程中 - 只要测试通过,您应该没问题(请记住使用与&#34;生产&#34;构建 - const_cast
相同的优化设置,这是强烈依赖于编译设置的事情之一。)