const_cast和std :: move从非引用中删除constness

时间:2016-05-24 15:51:10

标签: c++ c++11 const move-semantics

我有一个我无法修改的外部库。该库声明了一个模板函数,该函数由于某种原因返回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>()));
}

它编译并且整个代码按预期工作(不仅是上面的简化示例,而且我的真实代码也是如此)。

但是,它是安全的,还是我遇到了一些未定义的行为?有没有更好的解决方法?

我已阅读并理解有关从函数(12)返回const的问题,并且常见的答案似乎是返回const个对象是在现代C ++中不鼓励,但我的问题不是关于它,而是关于当外部库返回const对象时如何解决这种情况。

3 个答案:

答案 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;内存段的对象 - 如果fooconstexpr。然后调用移动构造函数(可能修改其参数)可能会导致访问冲突异常)。

您所能做的就是切换到不同的库(或者,如果可能的话,请求库维护人员修复API)或者创建一些单元测试并将其包含在您的构建过程中 - 只要测试通过,您应该没问题(请记住使用与&#34;生产&#34;构建 - const_cast相同的优化设置,这是强烈依赖于编译设置的事情之一。)