如何在第一次赋值后更改 const std::shared_ptr?
How to change const std::shared_ptr after first assignment?
如果我定义相同类型的shared_ptr
和const 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设计的手边。
...
现在,为什么会这样?实际 const
ness 被编译器用作优化提示。
{
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
引用并成为真正的构造函数.
写这篇文章有点痛苦,但可以做到。使用相同的代码进行序列化和反序列化更加痛苦(我无法理解如何)。
如果我定义相同类型的shared_ptr
和const 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设计的手边。
...
现在,为什么会这样?实际 const
ness 被编译器用作优化提示。
{
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
引用并成为真正的构造函数.
写这篇文章有点痛苦,但可以做到。使用相同的代码进行序列化和反序列化更加痛苦(我无法理解如何)。