如何在第一次赋值后更改 const std::shared_ptr?

How to change const std::shared_ptr after first assignment?

如果我定义相同类型的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 最终为 Empty,而 first 保持不变(例如引用计数仍然为 1)。

如何更改 const shared_ptr 最初设置后的值?这甚至可以用 std 的指针吗?

谢谢!

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

const std::shared_ptr<int> second

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

没有符合标准的方法来更改它在构建之后和销毁之前所指的内容。

话虽如此,手动调用析构函数并在同一位置构造新的 shared_ptr 可能是合法的,但我不确定。您绝对不能通过其原始名称引用 said shared_ptr,并且可能离开原始 shared_ptr 存在的范围是非法的(因为析构函数试图破坏 original 对象,编译器可以根据 const 对象的构造方式 证明 是一个空共享指针(或非空共享指针)。

即使您可以提出标准允许的论点,这也是一个坏主意。

const 个对象无法更改。

...

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

...

现在,为什么会这样?实际 constness 被编译器用作优化提示。

{
  const std::shared_ptr<int> bob = std::make_shared<int>();
}

在上面的例子中,编译器可以肯定知道bob在作用域的末尾是非空的。无法对 bob 执行任何操作,使它变空,但仍会保留已定义的行为。

所以编译器可以在销毁bob时删除范围末尾的分支,检查指针是否为空。

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

假设你把 bob 传给

void secret_code( std::shared_ptr<int> const& );

编译器无法查看 secret_code 的实现。可以假设密码不会编辑bob.

如果未声明 [​​=14=],secret_code 可以合法地对参数执行 const_cast<std::shared_ptr&> 并将其设置为 null;但是如果 secret_code 的参数实际上是 const 这是未定义的行为。 (任何放弃 const 的代码都负责保证不会通过这样做实际修改实际 const 值)

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

{
  const std::shared_ptr<int> bob = std::make_shared<int>();
  secret_code(bob);
  if (bob) {
    std::cout << "guaranteed to run"
  }
}

将打印 guaranteed to run 字符串。

bob 上使用 const,编译器可以自由地消除上面的 if 检查。

...

现在,请不要将我的解释混淆为 为什么 标准规定您不能使用 "if this doesn't happen there is no problem" 编辑 const 堆栈变量。标准规定你不应该这样做;这样做的后果是无限的,并且会随着编译器的新版本而增长。

...

来自评论:

For deserialize process, which is actually a type of constructor that deserialize object from file. C++ is nice, but it got its imperfections and sometimes its OK to search for less orthodox methods.

如果它是一个构造函数,让它成为一个构造函数

在 C++17 中,函数 returning a T 在许多方面基本上与真正的构造函数具有相同的地位(由于保证省略)。在 C++14 中,这并不完全正确(您还需要一个移动构造函数,并且编译器需要将其省略)。

因此 C++ 中类型 T 的反序列化构造函数需要 return 一个 T,它不能采用 T 引用并成为真正的构造函数.

写这篇文章有点痛苦,但可以做到。使用相同的代码进行序列化和反序列化更加痛苦(我无法理解如何)。