Sean Parent gave a talk at Going Native 2013 titled Inheritance Is The Base Class of Evil. At 20 minutes, 50 seconds in, he makes the statement that a shared pointer to an immutable (const) type (std::shared_pointer<const T>
) has value semantics. What exactly does this mean? Why is it any different than a shared pointer to a mutable (non-const) type (std::shared_pointer<T>
)?
-
4A guess: if we assume there are no raw pointers to non-const `T`, then the `const` "guarantees" the observable state of the object won't be modified. Therefore having multiple such pointers is as good as having multiple (immutable) objects (copies). – dyp Sep 10 '13 at 02:27
-
5This confused me as well. – Vittorio Romeo Sep 10 '13 at 06:18
4 Answers
Unfortunately, like all of the talks at Going Native 2013, it was constrained by a very tight time schedule. Fortunately for us, though, Sean Parent gave a much more thorough talk last year at C++Now called Value Semantics and Concepts-based Polymorphism. It covers the same material and will probably answer your questions. I'll take a shot at explaining anyway....
Introduction
There are two types of semantics that a type can have:
- Value semantics.
- Reference semantics. (Sometimes called pointer semantics.)
It is possible to go on for many pages about how the two differ and when one is preferable to the other. Let's simply say that code using value types can be more easily reasoned about.
That is to say that nothing unpredictable happens at any point to an instance of a value type -- something that cannot be guaranteed with a reference type since the value that is referenced is shared between other parts of your code that hold a reference to it.
In other words: reference types are less predictable because they can be changed by a distant piece of code. For example, a function that you call could change the value referenced out from under you. Or, even worse, if threading is involved, the reference type could be changed at any time by another thread that happens to operate on the value referenced. For this reason, Sean Parent makes the statement that a shared_ptr
is as good as a global variable when it comes to being able to reason about code that uses one.
With all of that said, we should be prepared to answer the question at hand.
Question and Answer
For a value type T
, why does a shared_ptr<const T>
act like a value type even though it is a pointer type?
Because we cannot make changes to the const T
that is pointed to, everything that was said about pointer/reference types being less predictable no longer applies. We no longer have to worry about the T
being changed unexpectedly because it is a const value type.
If we did want to make a change to the T
, we would have to make a copy of it, leaving others that hold a shared_ptr<const T>
unaffected by our actions. Moreover, the copy can even be hidden inside a value type using a mechanism called Copy-on-write, which seems to be what Sean Parent ultimately did.
I think I've answered the question as Sean Parent would (and did in the linked C++Now presentation), but lets go a bit further with an addendum.....
A Big Addendum:
(Thanks to @BretKuhns for bringing this up and providing an example in the comments.)
There is one nagging thing wrong with this whole notion. Saying that shared_ptr<const T>
behaves like a value type is not necesarily correct unless we know that all living pointers/references to that instance of T
are const
. This is because the const
modifier is a one way street -- holding a shared_ptr<const T>
may prevent us from modifying the instance of T
, but does not prevent somebody else from modifying the T
through a pointer/reference to non-const
.
Knowing this, I would be weary of making the broad statement that shared_ptr<const T>
is as good as a value type unless I knew that all living pointers to it are const
. But, knowing such a thing requires global knowledge of the code around all usages of shared_ptr<const T>
-- something that would not be a problem for a value type. For that reason, it might make more sense to say something like: A shared_ptr<const T>
can be used to support value semantics.
On a side note, I was actually at Going Native 2013 -- maybe you can see the back of my head in the front left.

- 1
- 1

- 6,979
- 1
- 37
- 50
-
3Note this is only true from the perspective of the library returning a `shared_ptr
`. Clients that receive a `shared_ptr – Bret Kuhns Sep 14 '13 at 16:42` cannot be guaranteed that the state of the `Foo` won't be mutated externally. See this code snippet for an example use case: https://gist.github.com/bkuhns/6563448 even though my `main()` holds a `shared_ptr `, the `Foo`'s `num()` changes state. This could be more subtle in a multi-threaded application where the state could change without the local call to `doSomething()` as in my example. -
@BretKuhns Agreed, and a very good point. Just because you hold a pointer/reference to const does not mean that everyone else does. However, a major point of the talk is to hide such details by making library code that exposes concrete value types to clients. The use of `shared_ptr
` then becomes an implementation detail of the library. – Sean Cline Sep 14 '13 at 17:05 -
@BretKuhns But on the subject of talks, don't forget Herb Sutter's recent "You Don't Know ___ and ___", where he means const and mutable. Including the standard-supported school of thought that these really mean "thread safe". So if the const methods you're calling on your sp
aren't thread safe then, according to this school of thought, they're in error. – Kaz Dragon Sep 16 '13 at 13:38 -
1@KazDragon A fair point, though that only means there shouldn't be any race conditions. Notice my gist isn't threaded but still demonstrates the problem. The two calls to get state from `foo` can be thread-safe, but the state can still change between the first and second call if another thread sneaks in and locks `foo` before you can call on it again. – Bret Kuhns Sep 16 '13 at 13:54
-
@BretKuhns As with any other sequential thread-based accesses, yes. Threads are hard :-/ – Kaz Dragon Sep 16 '13 at 13:58
-
Re the addendum: You can't create a `shared_ptr
` from a `shared_ptr – Mark Ransom Sep 16 '13 at 17:18` so you shouldn't need to worry about that. The `shared_ptr` contract is that it *owns* the pointer from that point forward; it would be a very poor design that allowed other non-const copies of the pointer to exist at the same time. -
@BretKuhns, I take it back. I had no idea `shared_ptr` was so flexible. – Mark Ransom Sep 16 '13 at 18:07
-
@MarkRansom I'm quite happy it is. It supports this type of conversion to make `shared_ptr
base = make_shared ();` possible. http://ideone.com/BEkAAx – Bret Kuhns Sep 16 '13 at 18:10 -
@BretKuhns, I always assumed you could make a `shared_ptr
` from a `derived*`, what surprised me is that you could do `shared_ptr ` from `shared_ptr `. Two different pointer types sharing the same reference count is counter-intuitive. – Mark Ransom Sep 16 '13 at 18:18 -
1@MarkRansom We're getting off topic, but I wouldn't call that surprising at all. The types differ, but they reference the same object, so it's natural that their reference counts *must* be shared. – Bret Kuhns Sep 16 '13 at 18:22
-
2I realize this is way after the fact, but I wanted to observe: in the addendum, you say that there is something wrong with "this whole notion". That's not quite right. It is quite correct that a shared_ptr to an immutable object has value semantics. But const T is not the same as an immutable object, exactly for the reasons discussed in the comments. const T is a type that can be used by a pointer/reference to refer to a non-const T by C++'s type system. If you create a type T with no non-const methods, that is an immutable type, and shared_ptr to it has value semantics. – Nir Friedman Apr 14 '16 at 14:44
I am giving 3 examples. In all three cases I create a variable a
with the content "original value"
. Then I create another variable b
by saying auto b = a;
and after this statement I assign a
the content "new value"
.
If a
and b
have value semantics, I expect b
's content to be the "original content"
. And in fact exactly this happens with string
and shared_ptr<const string>
. The conceptual meaning of auto b = a;
is the same with these types. Not so much with shared_ptr<string>
, b
will have the content "new value"
.
Code (online demo):
#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();
}
Output:
--------------
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
Having said that, I wish he had had a little more time: There are a few things I am still wondering about after that presentation. I am pretty convinced it was only the lack of time, he seems like a excellent presenter.

- 56,466
- 29
- 168
- 265
What he means is that they can be used to emulate value semantics.
The main defining trait of value semantics is that two objects with the same content are the same. Integers are value types: a 5 is the same as any other 5. Compare that to reference mechanics, where objects have an identity. A list a
containing [1, 2] is not the same as a list b
containing [1, 2], because appending 3 to a
does not have the same effect as appending 3 to b
. The identity of a
is different than the identity of b
.
This tends to be intuitive... it just sounds strange when put in words. Nobody makes it 3 days in C++ without getting some intuitive sense of value types vs. reference types.
If you have a mutable value type and you want to copy it, you have to actually copy the contents of the object. This is expensive.
The trick Sean is referring to is that if an object is immutable, then you don't have to copy the whole object, you can just refer to the old one. This is MUCH faster.

- 374,641
- 47
- 450
- 633

- 10,221
- 31
- 45
-
1Yet your explanations of value semantics are a bit off, I think. Of course appending 3 to `a` is the same as appending 3 to `b`, both are [1, 2, 3] then. And if appending 3 to `a` only, they of course have different values, in the same way as the two integers being 5 have different values when adding 1 to only one of them. In fact containers actually have value semantics, too. – Christian Rau Sep 10 '13 at 07:21
-
@ChristianRau, it's only "of course" because you're used to it. See Python for an example of how it could be different. – Mark Ransom Sep 13 '13 at 19:19
-
1I suppose I should be more verbose: "appending 3 to `a` does not have the same effect as appending 3 to `b`" should be more precicely written, "If you saw code to append 3 to list `a`, you could not blindly replace that code with new code that appends 3 to list `b` and expect the program to operate the same" – Cort Ammon Sep 14 '13 at 00:37
He seems to be assuming that the existence of a shared_ptr<const T>
implies that all handles to the object are also shared_ptr<const T>
(which is to say, read-only).
This is, of course, no more true than the existence of a raw const T*
constitutes proof that the object is const
.
Demo: http://ideone.com/UuHsEj
Probably you're mistaking "immutability" for meaning const T
-- in the question you said they're the same, but they aren't.

- 277,958
- 43
- 419
- 720
-
The difference between some non-owning `T const*` and Parent's use of `shared_ptr
` is that he actually controls the ownership. So he can in fact guarantee that the created objects are constant. – TemplateRex Sep 16 '13 at 09:10 -
@TemplateRex (does it ping you if I write TRex?) -- I'm not aware of any way to dynamically allocate `const` objects, and `shared_ptr` shouldn't be used with automatic or static objects, since the whole point of it is to dynamically deallocate the object when the reference count hits zero. – Ben Voigt Sep 16 '13 at 13:19
-
Not sure what you are saying: is the `shared_ptr
` not working code, or does it something else than what I think it does? (and I think only the auto-completed name pings me, just like BVoigt won't ping you - or does it?) – TemplateRex Sep 16 '13 at 13:41 -
@TemplateRex: I guess I've just never used `new` with a qualified type, but the Standard says it's legal "[ Note: the *type-id* may be a *cv-qualified* type, in which case the object created by the *new-expression* has a *cv-qualified* type. — end note ]" – Ben Voigt Sep 16 '13 at 14:15