C++ 指向不可变类型的共享指针具有值语义

C++ 指向不可变类型的共享指针具有值语义,c++,c++11,C++,C++11,肖恩·帕伦特在2013年“走向本土”大会上作了一次演讲。20分50秒后,他声明指向不可变(const)类型的共享指针(std::shared_pointer)具有值语义。这到底意味着什么?为什么它与指向可变(非常量)类型的共享指针(std::shared_pointer)有什么不同呢?他的意思是它们可以用来模拟值语义 价值语义学的主要定义特征是具有相同内容的两个对象是相同的。整数是值类型:5与任何其他5相同。将其与参考力学进行比较,在参考力学中,对象具有标识。包含[1,2]的列表A与包含[1,2

肖恩·帕伦特在2013年“走向本土”大会上作了一次演讲。20分50秒后,他声明指向不可变(const)类型的共享指针(
std::shared_pointer
)具有值语义。这到底意味着什么?为什么它与指向可变(非常量)类型的共享指针(
std::shared_pointer
)有什么不同呢?

他的意思是它们可以用来模拟值语义

价值语义学的主要定义特征是具有相同内容的两个对象是相同的。整数是值类型:5与任何其他5相同。将其与参考力学进行比较,在参考力学中,对象具有标识。包含[1,2]的列表
A
与包含[1,2]的列表
b
不同,因为将3添加到
A
与将3添加到
b
的效果不同。
a
的标识与
b
的标识不同

这往往是直观的。。。这听起来很奇怪。没有人在C++中使用3天,而没有得到一些对值类型与引用类型的直觉意义。

如果您有一个可变值类型,并且您想要复制它,那么您必须实际复制对象的内容。这个很贵


肖恩所指的技巧是,如果一个对象是不可变的,那么你不必复制整个对象,你只需引用旧对象即可。这要快得多。

我举三个例子。在这三种情况下,我都创建了一个变量
a
,其内容为
“原始值”
。然后我创建另一个变量
b
,方法是说
auto b=a在这个语句之后,我分配
a
内容
“新值”

如果
a
b
具有值语义,我希望
b
的内容是
的“原始内容”
。事实上,
string
shared\u ptr
就是这样。
autob=a的概念意义与这些类型相同。与
共享\u ptr
不同,
b
将包含
内容“新值”

代码():



话虽如此,我还是希望他有多一点时间:在那次演讲之后,我还有一些事情想知道。我非常确信这只是因为时间不够,他似乎是一个优秀的演讲者。

他似乎认为存在
共享的\u ptr
意味着对象的所有句柄也都是
共享的\u ptr
(也就是说,只读)

当然,这并不比原始
const T*
的存在构成了对象是
const
的证明更真实

演示:


也许你把“不变性”误认为意思是“常数”
——在问题中你说它们是一样的,但事实并非如此。

不幸的是,就像在上的所有会谈一样,它受到时间安排的限制。不过,对我们来说幸运的是,肖恩·帕伦特去年在C++会议上做了一次更为深入的演讲,这次会议名为“C++”。它涵盖了相同的材料,可能会回答您的问题。不管怎样,我还是要试着解释一下

介绍 类型可以具有两种类型的语义:

  • 价值语义学
  • 引用语义。(有时称为指针语义。)
我们可以用很多页的篇幅来讲述这两种方法的不同之处,以及何时一种方法优于另一种方法。让我们简单地说,使用值类型的代码更容易推理。

也就是说,值类型的实例在任何时候都不会发生任何不可预测的情况——引用类型无法保证这一点,因为引用的值是在代码中包含引用的其他部分之间共享的

换句话说:引用类型不太可预测,因为它们可以被一段遥远的代码更改。例如,您调用的函数可以更改从您下面引用的值。或者,更糟糕的是,如果涉及到线程,引用类型可能随时被另一个线程更改,而该线程恰好对引用的值进行操作。出于这个原因,Sean Parent声明,当涉及到能够对使用某个变量的代码进行推理时,a
shared_ptr
与全局变量一样好

话虽如此,我们应该准备好回答眼前的问题


问答 对于值类型
T
,为什么
shared\u ptr
即使是指针类型也像值类型一样工作?

因为我们无法更改指向的
常量
,所以所有关于指针/引用类型不可预测的说法都不再适用。我们不再需要担心
T
被意外更改,因为它是常量值类型

如果我们确实想对
T
进行更改,我们必须复制一份,让持有
共享\u ptr
的其他人不受我们行为的影响。此外,副本甚至可以使用一种称为的机制隐藏在值类型中,这似乎是Sean Parent最终所做的


我想我已经像Sean Parent一样回答了这个问题(在链接的C++Now演示文稿中也是如此),但是让我们进一步补充一下

一个大附录: (感谢您提出这一点,并在评论中提供了一个示例。)

这整个概念有一个令人烦恼的错误。说
shared\u ptr
的行为类似于值类型并不一定正确,除非我们知道
T
实例的所有活动指针/引用都是
const
。这是因为
const
修饰符是单向的——持有
shared\u ptr
可能会阻止我们修改
#include <iostream>
#include <memory>
#include <string>

using namespace std;

void string_example() {

    auto a = string("original value");

    auto b = a; // true copy by copying the value

    a = string("new value");

    cout << "a = " << a << endl;
    cout << "b = " << b << endl;
    cout << boolalpha << "&a == &b ? " << (&a==&b) << endl;
}

void shared_ptr_example() {

    auto a = make_shared<string>("original value");

    auto b = a; // not a copy, just and alias

    *a = string("new value"); // and this gonna hurt b

    cout << "a = " << *a << endl;
    cout << "b = " << *b << endl;
    cout << boolalpha << "&a == &b ? " << (&a==&b) << endl;
}

void shared_ptr_to_const_example() {

    auto a = make_shared<const string>("original value");

    auto b = a;

    //*a = string("new value"); // <-- now won't compile
    a = make_shared<const string>("new value");

    cout << "a = " << *a << endl;
    cout << "b = " << *b << endl;
    cout << boolalpha << "&a == &b ? " << (&a==&b) << endl;
}

int main() {

    cout << "--------------" << endl;
    cout << "string example" << endl;
    string_example();

    cout << "------------------" << endl;
    cout << "shared_ptr example" << endl;
    shared_ptr_example();

    cout << "---------------------------" << endl;
    cout << "shared_ptr to const example" << endl;
    shared_ptr_to_const_example();
}
--------------
string example
a = new value
b = original value
&a == &b ? false
------------------
shared_ptr example
a = new value
b = new value
&a == &b ? false
---------------------------
shared_ptr to const example
a = new value
b = original value
&a == &b ? false