如何在第一次分配后更改const std :: shared_ptr?

时间:2017-07-10 18:01:42

标签: c++ c++11 std shared-ptr smart-pointers

如果我定义了相同类型的shared_ptrconst shared_ptr,请执行以下操作:

std::shared_ptr<int> first = std::shared_ptr<int>(new int);
const std::shared_ptr<int> second = std::shared_ptr<int>();

稍后尝试像这样更改const shared_ptr的值:

second = first;

它会导致编译错误(应该如此)。但即使我试图抛弃const部分:

(std::shared_ptr<int>)second = first;

上面代码的结果是second最终为空,而first未被触及(例如,引用计数仍为1)。

如何在最初设置后更改const shared_ptr的值?这对std的指针来说甚至可能吗?

谢谢!

1 个答案:

答案 0 :(得分:3)

在构造或销毁之外以任何方式修改声明为const的变量是未定义的行为。

const std::shared_ptr<int> second

这是一个声明为const的变量。

在构造之后和销毁之前,没有标准的合规方式来改变它所指的内容。

话虽这么说,手动调用析构函数并在同一地点构建新的shared_ptr可能是合法的,我不确定。你肯定不能用它的原始名称来引用shared_ptr,并且可能离开原始shared_ptr存在的范围是非法的(因为析构函数试图破坏原始对象,根据{{​​1}}对象的构造方式,编译器可以证明的空共享指针(或非空指针)。

即使您可以在标准允许的情况下进行论证,这也是一个坏主意。

const个对象无法更改。

...

您投射到const只会创建一个临时副本。然后将其分配给,并更改临时副本。然后丢弃临时副本。未被修改的shared_ptr<int>是预期的行为。分配给临时副本的合法性是因为const shared_ptr<int>并且大多数shared_ptr库是在我们能够根据左边的r / lvalue-ness重载std之前设计的。手边。

...

现在,为什么会这样呢?编译器将实际operator= ness用作优化提示。

const

在上面的例子中,编译器可以知道某些{ const std::shared_ptr<int> bob = std::make_shared<int>(); } 在范围的末尾是非空的。 bob没有任何东西可以使它变空,但仍然会让你有明确的行为。

因此,在销毁检查指针是否为空的bob时,编译器可以消除作用域末尾的分支。

如果将bob传递给检查bob的空状态的inline函数,则可能会发生类似的优化;编译器可以省略检查。

假设您将bob传递给

bob

编译器无法看到void secret_code( std::shared_ptr<int> const& ); 的实现。它可以假设密码不会编辑bob

如果没有声明secret_codeconst可以合法地对参数执行secret_code并将其设置为null;但如果const_cast<std::shared_ptr&>的参数实际上是secret_code ,则这是未定义的行为。 (任何代码转换为const都有责任保证不会实际修改实际的const值)

const上没有const,编译器无法保证:

bob

将打印{ const std::shared_ptr<int> bob = std::make_shared<int>(); secret_code(bob); if (bob) { std::cout << "guaranteed to run" } } 字符串。

guaranteed to runconst,编译器可以自由地取消上面的bob检查。

...

现在,请不要混淆我的解释,因为为什么标准声明您无法使用&#34编辑if堆栈变量;如果没有发生则没有问题&# 34 ;.标准规定你不应该这样做;如果你这样做的后果是无限的,并且可以随着你的编译器的新版本而增长。

...

来自评论:

  

对于反序列化进程,它实际上是一种从文件反序列化对象的构造函数。 C ++很不错,但它有其不完美之处,有时可以搜索不那么正统的方法。

如果是构造函数,请将其设为构造函数

在C ++ 17中,返回const的函数在很多方面与真正的构造函数基本相同(由于保证省略)。在C ++ 14中,这不是真的(你还需要一个移动构造函数,编译器需要忽略它)。

因此,C ++中类型T的反序列化构造函数需要返回T,它不能通过引用获取T并且是真正的构造函数。

编写这个有点痛苦,但可以做到。使用相同的代码进行序列化和反序列化更是一种痛苦(我无法解决这些问题)。