Topic: Comments on propagate_const
Author: Hadrien Grasland <hadrien.grasland@gmail.com>
Date: Tue, 21 Feb 2017 06:54:55 -0800 (PST)
Raw View
------=_Part_744_862312118.1487688895297
Content-Type: multipart/alternative;
boundary="----=_Part_745_161024322.1487688895299"
------=_Part_745_161024322.1487688895299
Content-Type: text/plain; charset=UTF-8
Hi everyone,
I am not sure what this group's policy is regarding resurrection of old
threads, so I have decided to create a new one. But I would like to second
two comments that were previously made on the propagate_const proposal of
N4617:
1/ That it should be possible to copy a propagate_const<T> wrapper (
https://groups.google.com/a/isocpp.org/forum/#!searchin/std-proposals/propagate_const/std-proposals/1uDKcA9bssU/AZ-aQNAZAAAJ
).
2/ That propagate_const<T> should be convertible to a const-correct form of
the underlying pointer type (
https://groups.google.com/a/isocpp.org/forum/#!searchin/std-proposals/propagate_const/std-proposals/7rSMtvQVASk/niLSKtkcBwAJ
).
Let me motivate, through some concrete use cases, why I think that both of
these changes are necessary.
Need for shallow copy operations
There are many reasons to use pointers in C++. A very common use case,
however, is dynamic polymorphism:
- Different implementations of a virtual interface may have different
sizes
- C++, in its commendable quest for zero-cost abstraction, does not
provide native support for dynamically sized types
- When combined, these two language design choices lead to the
conclusion that if the set of implementations of an interface is not known
as compile time, polymorphism intrinsically requires pointer indirection in
C++
For this use case, pointer constness semantics are inadequate. If you use a
pointer-to-const, you cannot modify the target object, which is often fine
but tends to be excessively limitating for some use cases, particularly
when implementing constructors and factory functions. If, on the other
hand, you use a pointer-to-mutable, you open a const-correctness hole by
allowing yourself to mutate a const object without any compiler warning.
Quite frankly, none of these options are very appealing.
// Pointer-to-const restricts some valid use cases of polymorphism
class FirstTry
{
private:
std::unique_ptr<const Interface> m_ptr;
public:
FirstTry()
: m_ptr{ std::make_unique<Implementation>( ... ) }
{
m_ptr->setParent( *this ); // ERROR: Can't mutate through a const
pointer!
}
};
// Pointer-to-mutable breaks const correctness of polymorphism
class SecondTry
{
private:
std::unique_ptr<Interface> m_ptr;
public:
SecondTry()
: m_ptr{ std::make_unique<Implementation>( ... ) }
{
m_ptr->setParent( *this ); // This is now okay...
}
int someRandomAccessor() const {
m_ptr->setSomething(); // ...but unfortunately, this is okay too :-(
return 42;
}
};
Now, if we are on the same wavelength, and I will assume in the following
that this is the case, this is precisely the kind of issue that
propagate_const is designed to help with.
Let us now turn our attention to copy operations. Ideally, polymorphic
objects should be deep-copyable, just like any regular value type.
Unfortunately, in another application of the zero cost abstraction
principle, C++ provides no easy way to do this. Anyone who wants to combine
value semantics and dynamic polymorphism needs to provide explicit support
for deep copies across the entire class hierarchy, which is cumbersome to
begin with and can get problematic when interacting with third-party
libraries.
class Interface
{
public:
// This kind of boilerplate must be added to every class supporting deep
copy...
virtual std::unique_ptr<Interface> clone() const = 0;
};
class Implementation : public Interface
{
public:
// ...and replicated again and again in every single non-abstract child.
No fun.
std::unique_ptr<Interface> clone() const final override
{
return std::make_unique<Implementation>( *this );
}
};
But thankfully, sometimes, we can do without a true deep copy, and satisfy
ourselves with a shallow copy. And in fact, for some use cases, a shallow
copy will even be exactly what we want:
- When building some variety of search acceleration structure (hashmap,
neighbour locator, axis-aligned-bounding box...)
- When many "slave objects" share a reference to some common,
potentially large "master object"
Unfortunately, even though all standard C++ pointer types make it trivial
to shallow-copy a polymorphic object, propagate_const gets in our way here
by being move only. I see this design choice as a shortcoming, because it
reduces the usefulness of propagate_const in many dynamic polymorphism use
cases where the underlying pointer type would have done just fine. For
example, without shallow copy operations...
- You cannot let the compiler implement a shallow copy constructor for
you using "= default"
- You cannot use std::copy_if to find objects matching some predicate in
an internal dataset
- You cannot easily build object search acceleration structures, such as
hashmaps or AABBs
- You cannot easily share a reference to an object wrapped by
propagate_const with other objects
I am aware that shallow copies are possible using the get() and
get_underlying() operations. I do not see this as a satisfactory answer,
because it breaks every STL-ish algorithm that expects a copy constructor.
Of course, I could build a propagate_const wrapper that has a copy
constructor myself, and this is what I would end up doing if
propagate_const were accepted in the STL in its current form. But I think
that the use cases that I mentioned above are valid enough to warrant
changing the design of propagate_const instead.
I am aware that in order to be const-correct, a shallow copy constructor
for propagate_const would need to operate from a non-const reference. I am
fine with this tradeoff. The main reason we usually allow ourselves to make
mutable copies from const objects is because we assume the copy to be
independent from the original object. This assumption is broken when making
shallow copies through pointer or reference types. Sure, many APIs will be
broken initially, but that is unavoidable when fixing old programming
language flaws. Bugs will be reported, and interfaces will be fixed, the
way it's always been done.
For prior art, if you look at the STL's documentation, you will find that
customizable algorithms that copy const data, such as std::copy_if,
explicitly do NOT require the user-provided functions to consume the data
by const-reference. So the idea of copying from non-const is not new. The
C++ community only needs to re-discover it.
I am also aware of the objection, made in a thread linked above, that "We
were not confident that a non-const copy constructor would protect the user
from accidental loss of const and accidental mutable shared state". As far
as I'm concerned, this hand-waving statement was not properly justified.
From my perspective, the proposed alternative of using get_underlying seems
to be much bigger breach of const-correctness than a copy constructor that
operates from mutable, because from a semantic point of view,
get_underlying is essentially a silent const_cast that standard code
analysis tools won't catch.
Const-correct convertibility to the underlying pointer type
Sometimes, one has to extract a pointer back from the propagate_const
wrapper. I would see two main use cases for this:
- To feed a legacy API that is not (yet?) compatible with
propagate_const.
- To turn a propagate_const<T> into a pointer-to-const, when the
recipient should not be able to modify to the target object
The current propagate_const interface provides three ways to do this:
- Call get() and get a raw pointer
- Rely on an implicit cast to raw pointer, if available
- Use get_underlying to access the internals of propagate_const.
As I'm going to elaborate, the first two operations are not always the
right tool for the job at hand, because they fail to convey important
ownership information from the underlying pointer type. Whereas
get_underlying, as currently implemented, is a flawed interface that goes
directly against the design goals of propagate_const and should be
eradicated before standardization.
As a use case, suppose that as discussed previously, I am working a "slave
object", which holds a shared_ptr to an associated "master object" that it
shares with other slaves. To improve the const-correctness of this design,
I have decided to refactor the shared_ptr into a
propagate_const<shared_ptr>. So far, so good.
class Master
{
...
};
class Slave
{
private:
// This was refactored from "std::shared_ptr<Master> m_master"
std::propagate_const<std::shared_ptr<Master>> m_master;
public:
Slave( std::shared_ptr<Master> master )
: m_master(master)
{
m_master->addSlave( *this );
}
...
};
But as I proceed with the refactoring, I discover that the Slave class used
to provide a method that shares access to its master:
std::shared_ptr<Master> Slave::getMaster() const
{
return m_master;
}
This does not compile anymore. And I'm happy about that: it should never
have compiled to begin with. Returning non-const access to my members from
a const method definitely does not match my idea of const-correctness!
Instead, I would like to only provide const access to the master object,
like so:
std::shared_ptr<const Master> Slave::getMaster() const;
If my clients are well-behaved and do not mutate the master object, this
will be a minimally invasive interface change. It will require only minor
client rewrites, the kind that could be automated by sed or an IDE. It
could even require no client rewrite at all if I end up being lucky and
have clients that were using auto and friends.
Unfortunately, the current propagate_const interface does not allow me to
implement this method in a clean way.
The obvious code snippet would not work due to the lack of an appropriate
implicit conversion:
std::shared_ptr<const Master> Slave::getMaster() const
{
return m_master; // ERROR: No implicit conversion from
propagate_const<shared_ptr>!
}
Using get() here would be the perfect way to introduce use after free bugs:
std::shared_ptr<const Master> Slave::getMaster() const
{
const Master* master = m_master.get();
return std::shared_ptr<Master>( master ); // INCORRECT: Two owners for
one object!
}
And using get_underlying will both break const correctness and fail to
provide me with the correct return type:
std::shared_ptr<const Master> Slave::getMaster() const
{
auto master = std::get_underlying( m_master ); // This is an
std::shared_ptr<Master>
return master; // ERROR: No implicit conversion to std::shared_ptr<const
Master>!
}
To implement this method, in addition to the const-incorrect get_underlying
interface, I would also need a const cast!
std::shared_ptr<const Master> Slave::getMaster() const
{
auto master = std::get_underlying( m_master );
return std::const_pointer_cast<const Master>( master );
}
For sure, I would never want an abomination like this to pass code review :)
This little thought experiment showcases two major issues with
get_underlying:
- Counter to the goal of of propagate_const, it makes it trivial to
violate const-correctness without the compiler noticing (or, for that
matter, any developer or static analysis tool that is unfamiliar with the
propagate_const API).
- By not returning the const-correct type, get_underlying can make it
unnecessarily hard to implement const-correct interfaces.
The authors of the original propagate_const proposal were aware that the
get_underlying interface was suboptimal, and for this reason they opted to
make it less usable by hiding it as a free function instead of making it a
proper method of propagate_const. But personnally, I would go further: drop
get_underlying altogether, and replace it with something that is both
const-correct and respectful of pointer ownership issues.
My counter-proposal would be to have a propagate_const method, maybe called
"share()", which enables sharing access to the data pointed by
propagate_const with due respect paid to both const-correctness and the
underlying pointer's ownership semantics. In practice:
- If called on propagate_const<T*>, share() should return a T*
- If called on const propagate_const<T*>, share() should return a const
T*
- If called on propagate_const<shared_ptr<T>>, share() should return a
shared_ptr<T>
- If called on const propagate_const<shared_ptr<T>>, share() should
return a shared_ptr<const T>
- share() should not be defined on propagate_const<unique_ptr<T>> since
that pointer does not support sharing ownership
In addition, as a convenience, whenever a share() method exists, an
implicit cast could be provided to the underlying pointer type as a way to
make such ownership sharing more convenient and to enable things like
comparison of containers of shared_ptr<T> with containers of
propagate_const<shared_ptr<T>> through standard (STL-ish) algorithms.
If you really, positively want something like get_underlying to exist, it
should only be applicable to non-const object. This alone would make it
const-correct, and thus remove the need to implement it as a free function
and be careful when using it.
With this simple change, shameless people who want to get their hands dirty
and extract a mutable pointer from a const wrapper will then need to do
some extra work, as should be expected of them in my opinion:
using ConstPropagator = std::propagate_const<std::shared_ptr<T>>;
const ConstPropagator& immutableRef = ... ;
std::shared_ptr<T> mutableRef = const_cast<ConstPropagator*>( &immutableRef
)->get_underlying();
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/9ad3f22a-a619-4488-bf1e-303641c968b9%40isocpp.org.
------=_Part_745_161024322.1487688895299
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">Hi everyone,<br><br>I am not sure what this group's po=
licy is regarding resurrection of old threads, so I have decided to create =
a new one. But I would like to second two comments that were previously mad=
e on the propagate_const proposal of N4617:<br><br>1/ That it should be pos=
sible to copy a propagate_const<T> wrapper ( https://groups.google.co=
m/a/isocpp.org/forum/#!searchin/std-proposals/propagate_const/std-proposals=
/1uDKcA9bssU/AZ-aQNAZAAAJ ).<br>2/ That propagate_const<T> should be =
convertible to a const-correct form of the underlying pointer type ( https:=
//groups.google.com/a/isocpp.org/forum/#!searchin/std-proposals/propagate_c=
onst/std-proposals/7rSMtvQVASk/niLSKtkcBwAJ ).<br><br>Let me motivate, thro=
ugh some concrete use cases, why I think that both of these changes are nec=
essary.<br><br><br><font size=3D"4">Need for shallow copy operations</font>=
<br><br>There are many reasons to use pointers in C++. A very common use ca=
se, however, is dynamic polymorphism:<br><ul><li>Different implementations =
of a virtual interface may have different sizes</li><li>C++, in its commend=
able quest for zero-cost abstraction, does not provide native support for d=
ynamically sized types</li><li>When combined, these two language design cho=
ices lead to the conclusion that if the set of implementations of an interf=
ace is not known as compile time, polymorphism intrinsically requires point=
er indirection in C++</li></ul>For this use case, pointer constness semanti=
cs are inadequate. If you use a pointer-to-const, you cannot modify the tar=
get object, which is often fine but tends to be excessively limitating for =
some use cases, particularly when implementing constructors and factory fun=
ctions. If, on the other hand, you use a pointer-to-mutable, you open a con=
st-correctness hole by allowing yourself to mutate a const object without a=
ny compiler warning. Quite frankly, none of these options are very appealin=
g.<br><br><div style=3D"background-color: rgb(250, 250, 250); border-color:=
rgb(187, 187, 187); border-style: solid; border-width: 1px; overflow-wrap:=
break-word;" class=3D"prettyprint"><code class=3D"prettyprint"><div class=
=3D"subprettyprint"><span style=3D"color: #800;" class=3D"styled-by-prettif=
y">// Pointer-to-const restricts some valid use cases of polymorphism</span=
><span style=3D"color: #000;" class=3D"styled-by-prettify"><br></span><span=
style=3D"color: #008;" class=3D"styled-by-prettify">class</span><span styl=
e=3D"color: #000;" class=3D"styled-by-prettify"> </span><span style=3D"colo=
r: #606;" class=3D"styled-by-prettify">FirstTry</span><span style=3D"color:=
#000;" class=3D"styled-by-prettify"><br></span><span style=3D"color: #660;=
" class=3D"styled-by-prettify">{</span><span style=3D"color: #000;" class=
=3D"styled-by-prettify"></span><br><code class=3D"prettyprint"><span style=
=3D"color: #008;" class=3D"styled-by-prettify">private</span><span style=3D=
"color: #660;" class=3D"styled-by-prettify">:</span><span style=3D"color: #=
000;" class=3D"styled-by-prettify"><br>=C2=A0 std</span><span style=3D"colo=
r: #660;" class=3D"styled-by-prettify">::</span><span style=3D"color: #000;=
" class=3D"styled-by-prettify">unique_ptr</span><span style=3D"color: #660;=
" class=3D"styled-by-prettify"><</span><span style=3D"color: #008;" clas=
s=3D"styled-by-prettify">const</span><span style=3D"color: #000;" class=3D"=
styled-by-prettify"> </span><span style=3D"color: #606;" class=3D"styled-by=
-prettify">Interface</span><span style=3D"color: #660;" class=3D"styled-by-=
prettify">></span><span style=3D"color: #000;" class=3D"styled-by-pretti=
fy"> m_ptr</span><span style=3D"color: #660;" class=3D"styled-by-prettify">=
;</span><span style=3D"color: #000;" class=3D"styled-by-prettify"><br><br><=
/span></code><span style=3D"color: #008;" class=3D"styled-by-prettify">publ=
ic</span><span style=3D"color: #660;" class=3D"styled-by-prettify">:</span>=
<span style=3D"color: #000;" class=3D"styled-by-prettify"><br>=C2=A0 </span=
><span style=3D"color: #606;" class=3D"styled-by-prettify">FirstTry</span><=
span style=3D"color: #660;" class=3D"styled-by-prettify">()</span><span sty=
le=3D"color: #000;" class=3D"styled-by-prettify"><br>=C2=A0 =C2=A0 </span><=
span style=3D"color: #660;" class=3D"styled-by-prettify">:</span><span styl=
e=3D"color: #000;" class=3D"styled-by-prettify"> m_ptr</span><span style=3D=
"color: #660;" class=3D"styled-by-prettify">{</span><span style=3D"color: #=
000;" class=3D"styled-by-prettify"> std</span><span style=3D"color: #660;" =
class=3D"styled-by-prettify">::</span><span style=3D"color: #000;" class=3D=
"styled-by-prettify">make_unique</span><span style=3D"color: #660;" class=
=3D"styled-by-prettify"><</span><span style=3D"color: #606;" class=3D"st=
yled-by-prettify">Implementation</span><span style=3D"color: #660;" class=
=3D"styled-by-prettify">>(</span><span style=3D"color: #000;" class=3D"s=
tyled-by-prettify"> </span><span style=3D"color: #660;" class=3D"styled-by-=
prettify">...</span><span style=3D"color: #000;" class=3D"styled-by-prettif=
y"> </span><span style=3D"color: #660;" class=3D"styled-by-prettify">)</spa=
n><span style=3D"color: #000;" class=3D"styled-by-prettify"> </span><span s=
tyle=3D"color: #660;" class=3D"styled-by-prettify">}</span><span style=3D"c=
olor: #000;" class=3D"styled-by-prettify"><br>=C2=A0 </span><span style=3D"=
color: #660;" class=3D"styled-by-prettify">{</span><span style=3D"color: #0=
00;" class=3D"styled-by-prettify"><br>=C2=A0 =C2=A0 m_ptr</span><span style=
=3D"color: #660;" class=3D"styled-by-prettify">-></span><span style=3D"c=
olor: #000;" class=3D"styled-by-prettify">setParent</span><span style=3D"co=
lor: #660;" class=3D"styled-by-prettify">(</span><span style=3D"color: #000=
;" class=3D"styled-by-prettify"> </span><span style=3D"color: #660;" class=
=3D"styled-by-prettify">*</span><span style=3D"color: #008;" class=3D"style=
d-by-prettify">this</span><span style=3D"color: #000;" class=3D"styled-by-p=
rettify"> </span><span style=3D"color: #660;" class=3D"styled-by-prettify">=
);</span><span style=3D"color: #000;" class=3D"styled-by-prettify"> =C2=A0<=
/span><span style=3D"color: #800;" class=3D"styled-by-prettify">// ERROR:</=
span><span style=3D"color: #000;" class=3D"styled-by-prettify"> Can't m=
utate through a const pointer!<br>=C2=A0 </span><span style=3D"color: #660;=
" class=3D"styled-by-prettify">}</span><span style=3D"color: #000;" class=
=3D"styled-by-prettify"><br></span><span style=3D"color: #660;" class=3D"st=
yled-by-prettify">};</span><span style=3D"color: #000;" class=3D"styled-by-=
prettify"><br><br><br></span><span style=3D"color: #800;" class=3D"styled-b=
y-prettify">// Pointer-to-mutable breaks const correctness of polymorphism<=
/span><span style=3D"color: #000;" class=3D"styled-by-prettify"><br></span>=
<span style=3D"color: #008;" class=3D"styled-by-prettify">class</span><span=
style=3D"color: #000;" class=3D"styled-by-prettify"> </span><span style=3D=
"color: #606;" class=3D"styled-by-prettify">SecondTry</span><span style=3D"=
color: #000;" class=3D"styled-by-prettify"><br></span><span style=3D"color:=
#660;" class=3D"styled-by-prettify">{</span><span style=3D"color: #000;" c=
lass=3D"styled-by-prettify"><br>private:<br>=C2=A0 std::unique_ptr<Inter=
face> m_ptr;<br><br></span><span style=3D"color: #008;" class=3D"styled-=
by-prettify">public</span><span style=3D"color: #660;" class=3D"styled-by-p=
rettify">:</span><span style=3D"color: #000;" class=3D"styled-by-prettify">=
<br>=C2=A0 </span><span style=3D"color: #606;" class=3D"styled-by-prettify"=
>SecondTry</span><span style=3D"color: #660;" class=3D"styled-by-prettify">=
()</span><span style=3D"color: #000;" class=3D"styled-by-prettify"><br>=C2=
=A0 =C2=A0 </span><span style=3D"color: #660;" class=3D"styled-by-prettify"=
>:</span><span style=3D"color: #000;" class=3D"styled-by-prettify"> m_ptr</=
span><span style=3D"color: #660;" class=3D"styled-by-prettify">{</span><spa=
n style=3D"color: #000;" class=3D"styled-by-prettify"> std</span><span styl=
e=3D"color: #660;" class=3D"styled-by-prettify">::</span><span style=3D"col=
or: #000;" class=3D"styled-by-prettify">make_unique</span><span style=3D"co=
lor: #660;" class=3D"styled-by-prettify"><</span><span style=3D"color: #=
606;" class=3D"styled-by-prettify">Implementation</span><span style=3D"colo=
r: #660;" class=3D"styled-by-prettify">>(</span><span style=3D"color: #0=
00;" class=3D"styled-by-prettify"> </span><span style=3D"color: #660;" clas=
s=3D"styled-by-prettify">...</span><span style=3D"color: #000;" class=3D"st=
yled-by-prettify"> </span><span style=3D"color: #660;" class=3D"styled-by-p=
rettify">)</span><span style=3D"color: #000;" class=3D"styled-by-prettify">=
</span><span style=3D"color: #660;" class=3D"styled-by-prettify">}</span><=
span style=3D"color: #000;" class=3D"styled-by-prettify"><br>=C2=A0 </span>=
<span style=3D"color: #660;" class=3D"styled-by-prettify">{</span><span sty=
le=3D"color: #000;" class=3D"styled-by-prettify"><br>=C2=A0 =C2=A0 m_ptr</s=
pan><span style=3D"color: #660;" class=3D"styled-by-prettify">-></span><=
span style=3D"color: #000;" class=3D"styled-by-prettify">setParent</span><s=
pan style=3D"color: #660;" class=3D"styled-by-prettify">(</span><span style=
=3D"color: #000;" class=3D"styled-by-prettify"> </span><span style=3D"color=
: #660;" class=3D"styled-by-prettify">*</span><span style=3D"color: #008;" =
class=3D"styled-by-prettify">this</span><span style=3D"color: #000;" class=
=3D"styled-by-prettify"> </span><span style=3D"color: #660;" class=3D"style=
d-by-prettify">);</span><span style=3D"color: #000;" class=3D"styled-by-pre=
ttify"> =C2=A0</span><span style=3D"color: #800;" class=3D"styled-by-pretti=
fy">// This is now okay...</span><span style=3D"color: #000;" class=3D"styl=
ed-by-prettify"><br>=C2=A0 </span><span style=3D"color: #660;" class=3D"sty=
led-by-prettify">}</span><span style=3D"color: #000;" class=3D"styled-by-pr=
ettify"><br><br>=C2=A0 </span><span style=3D"color: #008;" class=3D"styled-=
by-prettify">int</span><span style=3D"color: #000;" class=3D"styled-by-pret=
tify"> someRandomAccessor</span><span style=3D"color: #660;" class=3D"style=
d-by-prettify">()</span><span style=3D"color: #000;" class=3D"styled-by-pre=
ttify"> </span><span style=3D"color: #008;" class=3D"styled-by-prettify">co=
nst</span><span style=3D"color: #000;" class=3D"styled-by-prettify"> </span=
><span style=3D"color: #660;" class=3D"styled-by-prettify">{</span><span st=
yle=3D"color: #000;" class=3D"styled-by-prettify"><br>=C2=A0 =C2=A0 m_ptr</=
span><span style=3D"color: #660;" class=3D"styled-by-prettify">-></span>=
<span style=3D"color: #000;" class=3D"styled-by-prettify">setSomething</spa=
n><span style=3D"color: #660;" class=3D"styled-by-prettify">();</span><span=
style=3D"color: #000;" class=3D"styled-by-prettify"> =C2=A0</span><span st=
yle=3D"color: #800;" class=3D"styled-by-prettify">// ...but unfortunately, =
this is okay too :-(</span><span style=3D"color: #000;" class=3D"styled-by-=
prettify"><br>=C2=A0 =C2=A0 </span><span style=3D"color: #008;" class=3D"st=
yled-by-prettify">return</span><span style=3D"color: #000;" class=3D"styled=
-by-prettify"> </span><span style=3D"color: #066;" class=3D"styled-by-prett=
ify">42</span><span style=3D"color: #660;" class=3D"styled-by-prettify">;</=
span><span style=3D"color: #000;" class=3D"styled-by-prettify"><br>=C2=A0 <=
/span><span style=3D"color: #660;" class=3D"styled-by-prettify">}</span><sp=
an style=3D"color: #000;" class=3D"styled-by-prettify"><br>};</span><span s=
tyle=3D"color: #000;" class=3D"styled-by-prettify"><br></span></div></code>=
</div><br>Now, if we are on the same wavelength, and I will assume in the f=
ollowing that this is the case, this is precisely the kind of issue that pr=
opagate_const is designed to help with.<br><br>Let us now turn our attentio=
n to copy operations. Ideally, polymorphic objects should be deep-copyable,=
just like any regular value type. Unfortunately, in another application of=
the zero cost abstraction principle, C++ provides no easy way to do this. =
Anyone who wants to combine value semantics and dynamic polymorphism needs =
to provide explicit support for deep copies across the entire class hierarc=
hy, which is cumbersome to begin with and can get problematic when interact=
ing with third-party libraries.<br><br><div style=3D"background-color: rgb(=
250, 250, 250); border-color: rgb(187, 187, 187); border-style: solid; bord=
er-width: 1px; overflow-wrap: break-word;" class=3D"prettyprint"><code clas=
s=3D"prettyprint"><div class=3D"subprettyprint"><span style=3D"color: #008;=
" class=3D"styled-by-prettify">class</span><span style=3D"color: #000;" cla=
ss=3D"styled-by-prettify"> </span><span style=3D"color: #606;" class=3D"sty=
led-by-prettify">Interface</span><span style=3D"color: #000;" class=3D"styl=
ed-by-prettify"><br></span><span style=3D"color: #660;" class=3D"styled-by-=
prettify">{</span><span style=3D"color: #000;" class=3D"styled-by-prettify"=
><br></span><span style=3D"color: #008;" class=3D"styled-by-prettify">publi=
c</span><span style=3D"color: #660;" class=3D"styled-by-prettify">:</span><=
span style=3D"color: #000;" class=3D"styled-by-prettify"><br>=C2=A0 </span>=
<span style=3D"color: #800;" class=3D"styled-by-prettify">// This kind of b=
oilerplate must be added to every class supporting deep copy...</span><span=
style=3D"color: #000;" class=3D"styled-by-prettify"><br>=C2=A0 </span><spa=
n style=3D"color: #008;" class=3D"styled-by-prettify">virtual</span><span s=
tyle=3D"color: #000;" class=3D"styled-by-prettify"> std</span><span style=
=3D"color: #660;" class=3D"styled-by-prettify">::</span><span style=3D"colo=
r: #000;" class=3D"styled-by-prettify">unique_ptr</span><span style=3D"colo=
r: #660;" class=3D"styled-by-prettify"><</span><span style=3D"color: #60=
6;" class=3D"styled-by-prettify">Interface</span><span style=3D"color: #660=
;" class=3D"styled-by-prettify">></span><span style=3D"color: #000;" cla=
ss=3D"styled-by-prettify"> clone</span><span style=3D"color: #660;" class=
=3D"styled-by-prettify">()</span><span style=3D"color: #000;" class=3D"styl=
ed-by-prettify"> </span><span style=3D"color: #008;" class=3D"styled-by-pre=
ttify">const</span><span style=3D"color: #000;" class=3D"styled-by-prettify=
"> </span><span style=3D"color: #660;" class=3D"styled-by-prettify">=3D</sp=
an><span style=3D"color: #000;" class=3D"styled-by-prettify"> </span><span =
style=3D"color: #066;" class=3D"styled-by-prettify">0</span><span style=3D"=
color: #660;" class=3D"styled-by-prettify">;</span><span style=3D"color: #0=
00;" class=3D"styled-by-prettify"><br></span><span style=3D"color: #660;" c=
lass=3D"styled-by-prettify">};</span><span style=3D"color: #000;" class=3D"=
styled-by-prettify"><br><br></span><span style=3D"color: #008;" class=3D"st=
yled-by-prettify">class</span><span style=3D"color: #000;" class=3D"styled-=
by-prettify"> </span><span style=3D"color: #606;" class=3D"styled-by-pretti=
fy">Implementation</span><span style=3D"color: #000;" class=3D"styled-by-pr=
ettify"> </span><span style=3D"color: #660;" class=3D"styled-by-prettify">:=
</span><span style=3D"color: #000;" class=3D"styled-by-prettify"> </span><s=
pan style=3D"color: #008;" class=3D"styled-by-prettify">public</span><span =
style=3D"color: #000;" class=3D"styled-by-prettify"> </span><span style=3D"=
color: #606;" class=3D"styled-by-prettify">Interface</span><span style=3D"c=
olor: #000;" class=3D"styled-by-prettify"><br></span><span style=3D"color: =
#660;" class=3D"styled-by-prettify">{</span><span style=3D"color: #000;" cl=
ass=3D"styled-by-prettify"><br></span><span style=3D"color: #008;" class=3D=
"styled-by-prettify">public</span><span style=3D"color: #660;" class=3D"sty=
led-by-prettify">:</span><span style=3D"color: #000;" class=3D"styled-by-pr=
ettify"><br>=C2=A0 </span><span style=3D"color: #800;" class=3D"styled-by-p=
rettify">// ...and replicated again and again in every single non-abstract =
child. No fun.</span><span style=3D"color: #000;" class=3D"styled-by-pretti=
fy"><br>=C2=A0 std</span><span style=3D"color: #660;" class=3D"styled-by-pr=
ettify">::</span><span style=3D"color: #000;" class=3D"styled-by-prettify">=
unique_ptr</span><span style=3D"color: #660;" class=3D"styled-by-prettify">=
<</span><span style=3D"color: #606;" class=3D"styled-by-prettify">Interf=
ace</span><span style=3D"color: #660;" class=3D"styled-by-prettify">></s=
pan><span style=3D"color: #000;" class=3D"styled-by-prettify"> clone</span>=
<span style=3D"color: #660;" class=3D"styled-by-prettify">()</span><span st=
yle=3D"color: #000;" class=3D"styled-by-prettify"> </span><span style=3D"co=
lor: #008;" class=3D"styled-by-prettify">const</span><span style=3D"color: =
#000;" class=3D"styled-by-prettify"> </span><span style=3D"color: #008;" cl=
ass=3D"styled-by-prettify">final</span><span style=3D"color: #000;" class=
=3D"styled-by-prettify"> </span><span style=3D"color: #008;" class=3D"style=
d-by-prettify">override</span><span style=3D"color: #000;" class=3D"styled-=
by-prettify"><br>=C2=A0 </span><span style=3D"color: #660;" class=3D"styled=
-by-prettify">{</span><span style=3D"color: #000;" class=3D"styled-by-prett=
ify"><br>=C2=A0 =C2=A0 </span><span style=3D"color: #008;" class=3D"styled-=
by-prettify">return</span><span style=3D"color: #000;" class=3D"styled-by-p=
rettify"> std</span><span style=3D"color: #660;" class=3D"styled-by-prettif=
y">::</span><span style=3D"color: #000;" class=3D"styled-by-prettify">make_=
unique</span><span style=3D"color: #660;" class=3D"styled-by-prettify"><=
</span><span style=3D"color: #606;" class=3D"styled-by-prettify">Implementa=
tion</span><span style=3D"color: #660;" class=3D"styled-by-prettify">>(<=
/span><span style=3D"color: #000;" class=3D"styled-by-prettify"> </span><sp=
an style=3D"color: #660;" class=3D"styled-by-prettify">*</span><span style=
=3D"color: #008;" class=3D"styled-by-prettify">this</span><span style=3D"co=
lor: #000;" class=3D"styled-by-prettify"> </span><span style=3D"color: #660=
;" class=3D"styled-by-prettify">);</span><span style=3D"color: #000;" class=
=3D"styled-by-prettify"><br>=C2=A0 </span><span style=3D"color: #660;" clas=
s=3D"styled-by-prettify">}</span><span style=3D"color: #000;" class=3D"styl=
ed-by-prettify"><br></span><span style=3D"color: #660;" class=3D"styled-by-=
prettify">};</span></div></code></div><br>But thankfully, sometimes, we can=
do without a true deep copy, and satisfy ourselves with a shallow copy. An=
d in fact, for some use cases, a shallow copy will even be exactly what we =
want:<br><ul><li>When building some variety of search acceleration structur=
e (hashmap, neighbour locator, axis-aligned-bounding box...)</li><li>When m=
any "slave objects" share a reference to some common, potentially=
large "master object"</li></ul>Unfortunately, even though all st=
andard C++ pointer types make it trivial to shallow-copy a polymorphic obje=
ct, propagate_const gets in our way here by being move only. I see this des=
ign choice as a shortcoming, because it reduces the usefulness of propagate=
_const in many dynamic polymorphism use cases where the underlying pointer =
type would have done just fine. For example, without shallow copy operation=
s...<br><ul><li>You cannot let the compiler implement a shallow copy constr=
uctor for you using "=3D default"<br></li><li>You cannot use std:=
:copy_if to find objects matching some predicate in an internal dataset</li=
><li>You cannot easily build object search acceleration structures, such as=
hashmaps or AABBs</li><li>You cannot easily share a reference to an object=
wrapped by propagate_const with other objects</li></ul><p>I am aware that =
shallow copies are possible using the get() and get_underlying() operations=
.. I do not see this as a satisfactory answer, because it breaks every STL-i=
sh algorithm that expects a copy constructor. Of course, I could build a pr=
opagate_const wrapper that has a copy constructor myself, and this is what =
I would end up doing if propagate_const were accepted in the STL in its cur=
rent form. But I think that the use cases that I mentioned above are valid =
enough to warrant changing the design of propagate_const instead.</p><p><br=
></p><p>I am aware that in order to be const-correct, a shallow copy constr=
uctor for propagate_const would need to operate from a non-const reference.=
I am fine with this tradeoff. The main reason we usually allow ourselves t=
o make mutable copies from const objects is because we assume the copy to b=
e independent from the original object. This assumption is broken when maki=
ng shallow copies through pointer or reference types. Sure, many APIs will =
be broken initially, but that is unavoidable when fixing old programming la=
nguage flaws. Bugs will be reported, and interfaces will be fixed, the way =
it's always been done.<br></p><p><br></p><p>For prior art, if you look =
at the STL's documentation, you will find that customizable algorithms =
that copy const data, such as std::copy_if, explicitly do NOT require the u=
ser-provided functions to consume the data by const-reference. So the idea =
of copying from non-const is not new. The C++ community only needs to re-di=
scover it.<br></p><p><br></p><p>I am also aware of the objection, made in a=
thread linked above, that "We were not confident that a non-const cop=
y constructor would protect=20
the user from accidental loss of const and accidental mutable shared=20
state". As far as I'm concerned, this hand-waving statement was no=
t properly justified. From my perspective, the proposed alternative of usin=
g get_underlying seems to be much bigger breach of const-correctness than a=
copy constructor that operates from mutable, because from a semantic point=
of view, get_underlying is essentially a silent const_cast that standard c=
ode analysis tools won't catch.</p><p><br></p><br><font size=3D"4">Cons=
t-correct convertibility to the underlying pointer type</font><br><br>Somet=
imes, one has to extract a pointer back from the propagate_const wrapper. I=
would see two main use cases for this:<br><ul><li>To feed a legacy API tha=
t is not (yet?) compatible with propagate_const.<br></li><li>To turn a prop=
agate_const<T> into a pointer-to-const, when the recipient should not=
be able to modify to the target object</li></ul><p>The current propagate_c=
onst interface provides three ways to do this:</p><ul><li>Call get() and ge=
t a raw pointer</li><li>Rely on an implicit cast to raw pointer, if availab=
le</li><li>Use get_underlying to access the internals of propagate_const.</=
li></ul><p>As I'm going to elaborate, the first two operations are not =
always the right tool for the job at hand, because they fail to convey impo=
rtant ownership information from the underlying pointer type. Whereas get_u=
nderlying, as currently implemented, is a flawed interface that goes direct=
ly against the design goals of propagate_const and should be eradicated bef=
ore standardization.</p><p><br></p><p>As a use case, suppose that as discus=
sed previously, I am working a "slave object", which holds a shar=
ed_ptr to an associated "master object" that it shares with other=
slaves. To improve the const-correctness of this design, I have decided to=
refactor the shared_ptr into a propagate_const<shared_ptr>. So far, =
so good.<br></p><p><br></p><p></p><div style=3D"background-color: rgb(250, =
250, 250); border-color: rgb(187, 187, 187); border-style: solid; border-wi=
dth: 1px; overflow-wrap: break-word;" class=3D"prettyprint"><code class=3D"=
prettyprint"><div class=3D"subprettyprint"><span style=3D"color: #008;" cla=
ss=3D"styled-by-prettify">class</span><span style=3D"color: #000;" class=3D=
"styled-by-prettify"> </span><span style=3D"color: #606;" class=3D"styled-b=
y-prettify">Master</span><span style=3D"color: #000;" class=3D"styled-by-pr=
ettify"><br></span><span style=3D"color: #660;" class=3D"styled-by-prettify=
">{</span><span style=3D"color: #000;" class=3D"styled-by-prettify"><br>=C2=
=A0 </span><span style=3D"color: #660;" class=3D"styled-by-prettify">...</s=
pan><span style=3D"color: #000;" class=3D"styled-by-prettify"><br></span><s=
pan style=3D"color: #660;" class=3D"styled-by-prettify">};</span><span styl=
e=3D"color: #000;" class=3D"styled-by-prettify"><br><br></span><span style=
=3D"color: #008;" class=3D"styled-by-prettify">class</span><span style=3D"c=
olor: #000;" class=3D"styled-by-prettify"> </span><span style=3D"color: #60=
6;" class=3D"styled-by-prettify">Slave</span><span style=3D"color: #000;" c=
lass=3D"styled-by-prettify"><br></span><span style=3D"color: #660;" class=
=3D"styled-by-prettify">{</span><span style=3D"color: #000;" class=3D"style=
d-by-prettify"><br></span><span style=3D"color: #008;" class=3D"styled-by-p=
rettify">private</span><span style=3D"color: #660;" class=3D"styled-by-pret=
tify">:</span><span style=3D"color: #000;" class=3D"styled-by-prettify"><br=
>=C2=A0 </span><span style=3D"color: #800;" class=3D"styled-by-prettify">//=
This was refactored from "std::shared_ptr<Master> m_master"=
;</span><span style=3D"color: #000;" class=3D"styled-by-prettify"><br>=C2=
=A0 std</span><span style=3D"color: #660;" class=3D"styled-by-prettify">::<=
/span><span style=3D"color: #000;" class=3D"styled-by-prettify">propagate_c=
onst</span><span style=3D"color: #660;" class=3D"styled-by-prettify"><</=
span><span style=3D"color: #000;" class=3D"styled-by-prettify">std</span><s=
pan style=3D"color: #660;" class=3D"styled-by-prettify">::</span><span styl=
e=3D"color: #000;" class=3D"styled-by-prettify">shared_ptr</span><span styl=
e=3D"color: #660;" class=3D"styled-by-prettify"><</span><span style=3D"c=
olor: #606;" class=3D"styled-by-prettify">Master</span><span style=3D"color=
: #660;" class=3D"styled-by-prettify">>></span><span style=3D"color: =
#000;" class=3D"styled-by-prettify"> m_master</span><span style=3D"color: #=
660;" class=3D"styled-by-prettify">;</span><span style=3D"color: #000;" cla=
ss=3D"styled-by-prettify"><br><br></span><span style=3D"color: #008;" class=
=3D"styled-by-prettify">public</span><span style=3D"color: #660;" class=3D"=
styled-by-prettify">:</span><span style=3D"color: #000;" class=3D"styled-by=
-prettify"><br>=C2=A0 </span><span style=3D"color: #606;" class=3D"styled-b=
y-prettify">Slave</span><span style=3D"color: #660;" class=3D"styled-by-pre=
ttify">(</span><span style=3D"color: #000;" class=3D"styled-by-prettify"> s=
td</span><span style=3D"color: #660;" class=3D"styled-by-prettify">::</span=
><span style=3D"color: #000;" class=3D"styled-by-prettify">shared_ptr</span=
><span style=3D"color: #660;" class=3D"styled-by-prettify"><</span><span=
style=3D"color: #606;" class=3D"styled-by-prettify">Master</span><span sty=
le=3D"color: #660;" class=3D"styled-by-prettify">></span><span style=3D"=
color: #000;" class=3D"styled-by-prettify"> master </span><span style=3D"co=
lor: #660;" class=3D"styled-by-prettify">)</span><span style=3D"color: #000=
;" class=3D"styled-by-prettify"><br>=C2=A0 =C2=A0 </span><span style=3D"col=
or: #660;" class=3D"styled-by-prettify">:</span><span style=3D"color: #000;=
" class=3D"styled-by-prettify"> m_master</span><span style=3D"color: #660;"=
class=3D"styled-by-prettify">(</span><span style=3D"color: #000;" class=3D=
"styled-by-prettify">master</span><span style=3D"color: #660;" class=3D"sty=
led-by-prettify">)</span><span style=3D"color: #000;" class=3D"styled-by-pr=
ettify"><br>=C2=A0 </span><span style=3D"color: #660;" class=3D"styled-by-p=
rettify">{</span><span style=3D"color: #000;" class=3D"styled-by-prettify">=
<br>=C2=A0 =C2=A0 m_master</span><span style=3D"color: #660;" class=3D"styl=
ed-by-prettify">-></span><span style=3D"color: #000;" class=3D"styled-by=
-prettify">addSlave</span><span style=3D"color: #660;" class=3D"styled-by-p=
rettify">(</span><span style=3D"color: #000;" class=3D"styled-by-prettify">=
</span><span style=3D"color: #660;" class=3D"styled-by-prettify">*</span><=
span style=3D"color: #008;" class=3D"styled-by-prettify">this</span><span s=
tyle=3D"color: #000;" class=3D"styled-by-prettify"> </span><span style=3D"c=
olor: #660;" class=3D"styled-by-prettify">);</span><span style=3D"color: #0=
00;" class=3D"styled-by-prettify"><br>=C2=A0 </span><span style=3D"color: #=
660;" class=3D"styled-by-prettify">}</span><span style=3D"color: #000;" cla=
ss=3D"styled-by-prettify"><br><br>=C2=A0 ...<br></span><span style=3D"color=
: #660;" class=3D"styled-by-prettify">};</span></div></code></div><br><p></=
p>But as I proceed with the refactoring, I discover that the Slave class us=
ed to provide a method that shares access to its master:<br><br><div style=
=3D"background-color: rgb(250, 250, 250); border-color: rgb(187, 187, 187);=
border-style: solid; border-width: 1px; overflow-wrap: break-word;" class=
=3D"prettyprint"><code class=3D"prettyprint"><div class=3D"subprettyprint">=
<span style=3D"color: #000;" class=3D"styled-by-prettify">std</span><span s=
tyle=3D"color: #660;" class=3D"styled-by-prettify">::</span><span style=3D"=
color: #000;" class=3D"styled-by-prettify">shared_ptr</span><span style=3D"=
color: #660;" class=3D"styled-by-prettify"><</span><span style=3D"color:=
#606;" class=3D"styled-by-prettify">Master</span><span style=3D"color: #66=
0;" class=3D"styled-by-prettify">></span><span style=3D"color: #000;" cl=
ass=3D"styled-by-prettify"> </span><span style=3D"color: #606;" class=3D"st=
yled-by-prettify">Slave</span><span style=3D"color: #660;" class=3D"styled-=
by-prettify">::</span><span style=3D"color: #000;" class=3D"styled-by-prett=
ify">getMaster</span><span style=3D"color: #660;" class=3D"styled-by-pretti=
fy">()</span><span style=3D"color: #000;" class=3D"styled-by-prettify"> </s=
pan><span style=3D"color: #008;" class=3D"styled-by-prettify">const</span><=
span style=3D"color: #000;" class=3D"styled-by-prettify"><br></span><span s=
tyle=3D"color: #660;" class=3D"styled-by-prettify">{</span><span style=3D"c=
olor: #000;" class=3D"styled-by-prettify"><br>=C2=A0 </span><span style=3D"=
color: #008;" class=3D"styled-by-prettify">return</span><span style=3D"colo=
r: #000;" class=3D"styled-by-prettify"> m_master</span><span style=3D"color=
: #660;" class=3D"styled-by-prettify">;</span><span style=3D"color: #000;" =
class=3D"styled-by-prettify"><br></span><span style=3D"color: #660;" class=
=3D"styled-by-prettify">}</span></div></code></div><br>This does not compil=
e anymore. And I'm happy about that: it should never have compiled to b=
egin with. Returning non-const access to my members from a const method def=
initely does not match my idea of const-correctness!<br><br>Instead, I woul=
d like to only provide const access to the master object, like so:<br><br><=
div style=3D"background-color: rgb(250, 250, 250); border-color: rgb(187, 1=
87, 187); border-style: solid; border-width: 1px; overflow-wrap: break-word=
;" class=3D"prettyprint"><code class=3D"prettyprint"><div class=3D"subprett=
yprint"><span style=3D"color: #000;" class=3D"styled-by-prettify">std</span=
><span style=3D"color: #660;" class=3D"styled-by-prettify">::</span><span s=
tyle=3D"color: #000;" class=3D"styled-by-prettify">shared_ptr</span><span s=
tyle=3D"color: #660;" class=3D"styled-by-prettify"><</span><span style=
=3D"color: #008;" class=3D"styled-by-prettify">const</span><span style=3D"c=
olor: #000;" class=3D"styled-by-prettify"> </span><span style=3D"color: #60=
6;" class=3D"styled-by-prettify">Master</span><span style=3D"color: #660;" =
class=3D"styled-by-prettify">></span><span style=3D"color: #000;" class=
=3D"styled-by-prettify"> </span><span style=3D"color: #606;" class=3D"style=
d-by-prettify">Slave</span><span style=3D"color: #660;" class=3D"styled-by-=
prettify">::</span><span style=3D"color: #000;" class=3D"styled-by-prettify=
">getMaster</span><span style=3D"color: #660;" class=3D"styled-by-prettify"=
>()</span><span style=3D"color: #000;" class=3D"styled-by-prettify"> </span=
><span style=3D"color: #008;" class=3D"styled-by-prettify">const</span><spa=
n style=3D"color: #660;" class=3D"styled-by-prettify">;</span></div></code>=
</div><br>If my clients are well-behaved and do not mutate the master objec=
t, this will be a minimally invasive interface change. It will require only=
minor client rewrites, the kind that could be automated by sed or an IDE. =
It could even require no client rewrite at all if I end up being lucky and =
have clients that were using auto and friends.<br><br>Unfortunately, the cu=
rrent propagate_const interface does not allow me to implement this method =
in a clean way.<br><br>The obvious code snippet would not work due to the l=
ack of an appropriate implicit conversion:<br><br><div style=3D"background-=
color: rgb(250, 250, 250); border-color: rgb(187, 187, 187); border-style: =
solid; border-width: 1px; overflow-wrap: break-word;" class=3D"prettyprint"=
><code class=3D"prettyprint"><div class=3D"subprettyprint"><span style=3D"c=
olor: #000;" class=3D"styled-by-prettify">std</span><span style=3D"color: #=
660;" class=3D"styled-by-prettify">::</span><span style=3D"color: #000;" cl=
ass=3D"styled-by-prettify">shared_ptr</span><span style=3D"color: #660;" cl=
ass=3D"styled-by-prettify"><const </span><span style=3D"color: #606;" cl=
ass=3D"styled-by-prettify">Master</span><span style=3D"color: #660;" class=
=3D"styled-by-prettify">></span><span style=3D"color: #000;" class=3D"st=
yled-by-prettify"> </span><span style=3D"color: #606;" class=3D"styled-by-p=
rettify">Slave</span><span style=3D"color: #660;" class=3D"styled-by-pretti=
fy">::</span><span style=3D"color: #000;" class=3D"styled-by-prettify">getM=
aster</span><span style=3D"color: #660;" class=3D"styled-by-prettify">()</s=
pan><span style=3D"color: #000;" class=3D"styled-by-prettify"> </span><span=
style=3D"color: #008;" class=3D"styled-by-prettify">const</span><span styl=
e=3D"color: #000;" class=3D"styled-by-prettify"><br></span><span style=3D"c=
olor: #660;" class=3D"styled-by-prettify">{</span><span style=3D"color: #00=
0;" class=3D"styled-by-prettify"><br>=C2=A0 </span><span style=3D"color: #0=
08;" class=3D"styled-by-prettify">return</span><span style=3D"color: #000;"=
class=3D"styled-by-prettify"> m_master</span><span style=3D"color: #660;" =
class=3D"styled-by-prettify">;</span><span style=3D"color: #000;" class=3D"=
styled-by-prettify">=C2=A0 // ERROR: No implicit conversion from propagate_=
const<shared_ptr>!<br></span><span style=3D"color: #660;" class=3D"st=
yled-by-prettify">}</span></div></code></div><br>Using get() here would be =
the perfect way to introduce use after free bugs:<br><br><div style=3D"back=
ground-color: rgb(250, 250, 250); border-color: rgb(187, 187, 187); border-=
style: solid; border-width: 1px; overflow-wrap: break-word;" class=3D"prett=
yprint"><code class=3D"prettyprint"><div class=3D"subprettyprint"><span sty=
le=3D"color: #000;" class=3D"styled-by-prettify">std</span><span style=3D"c=
olor: #660;" class=3D"styled-by-prettify">::</span><span style=3D"color: #0=
00;" class=3D"styled-by-prettify">shared_ptr</span><span style=3D"color: #6=
60;" class=3D"styled-by-prettify"><const </span><span style=3D"color: #6=
06;" class=3D"styled-by-prettify">Master</span><span style=3D"color: #660;"=
class=3D"styled-by-prettify">></span><span style=3D"color: #000;" class=
=3D"styled-by-prettify"> </span><span style=3D"color: #606;" class=3D"style=
d-by-prettify">Slave</span><span style=3D"color: #660;" class=3D"styled-by-=
prettify">::</span><span style=3D"color: #000;" class=3D"styled-by-prettify=
">getMaster</span><span style=3D"color: #660;" class=3D"styled-by-prettify"=
>()</span><span style=3D"color: #000;" class=3D"styled-by-prettify"> </span=
><span style=3D"color: #008;" class=3D"styled-by-prettify">const</span><spa=
n style=3D"color: #000;" class=3D"styled-by-prettify"><br></span><span styl=
e=3D"color: #660;" class=3D"styled-by-prettify">{</span><span style=3D"colo=
r: #000;" class=3D"styled-by-prettify"><br>=C2=A0 const Master* master =3D =
m_master.get();<br>=C2=A0 </span><span style=3D"color: #008;" class=3D"styl=
ed-by-prettify">return</span><span style=3D"color: #000;" class=3D"styled-b=
y-prettify"> std::shared_ptr<Master>( master</span><span style=3D"col=
or: #660;" class=3D"styled-by-prettify"> );</span><span style=3D"color: #00=
0;" class=3D"styled-by-prettify">=C2=A0 // INCORRECT: Two owners for one ob=
ject!<br></span><span style=3D"color: #660;" class=3D"styled-by-prettify">}=
</span></div></code></div><br>And using get_underlying will both break cons=
t correctness and fail to provide me with the correct return type:<br><br><=
div style=3D"background-color: rgb(250, 250, 250); border-color: rgb(187, 1=
87, 187); border-style: solid; border-width: 1px; overflow-wrap: break-word=
;" class=3D"prettyprint"><code class=3D"prettyprint"><div class=3D"subprett=
yprint"><span style=3D"color: #000;" class=3D"styled-by-prettify">std</span=
><span style=3D"color: #660;" class=3D"styled-by-prettify">::</span><span s=
tyle=3D"color: #000;" class=3D"styled-by-prettify">shared_ptr</span><span s=
tyle=3D"color: #660;" class=3D"styled-by-prettify"><const </span><span s=
tyle=3D"color: #606;" class=3D"styled-by-prettify">Master</span><span style=
=3D"color: #660;" class=3D"styled-by-prettify">></span><span style=3D"co=
lor: #000;" class=3D"styled-by-prettify"> </span><span style=3D"color: #606=
;" class=3D"styled-by-prettify">Slave</span><span style=3D"color: #660;" cl=
ass=3D"styled-by-prettify">::</span><span style=3D"color: #000;" class=3D"s=
tyled-by-prettify">getMaster</span><span style=3D"color: #660;" class=3D"st=
yled-by-prettify">()</span><span style=3D"color: #000;" class=3D"styled-by-=
prettify"> </span><span style=3D"color: #008;" class=3D"styled-by-prettify"=
>const</span><span style=3D"color: #000;" class=3D"styled-by-prettify"><br>=
</span><span style=3D"color: #660;" class=3D"styled-by-prettify">{</span><s=
pan style=3D"color: #000;" class=3D"styled-by-prettify"><br>=C2=A0 auto mas=
ter =3D std::get_underlying( m_master );=C2=A0 // This is an std::shared_pt=
r<Master><br>=C2=A0 </span><span style=3D"color: #008;" class=3D"styl=
ed-by-prettify">return</span><span style=3D"color: #000;" class=3D"styled-b=
y-prettify"> master;=C2=A0 // ERROR: No implicit conversion to std::shared_=
ptr<const Master>!</span><span style=3D"color: #000;" class=3D"styled=
-by-prettify"><br></span><span style=3D"color: #660;" class=3D"styled-by-pr=
ettify">}</span></div></code></div><br>To implement this method, in additio=
n to the const-incorrect get_underlying interface, I would also need a cons=
t cast!<br><br><div style=3D"background-color: rgb(250, 250, 250); border-c=
olor: rgb(187, 187, 187); border-style: solid; border-width: 1px; overflow-=
wrap: break-word;" class=3D"prettyprint"><code class=3D"prettyprint"><div c=
lass=3D"subprettyprint"><span style=3D"color: #000;" class=3D"styled-by-pre=
ttify">std</span><span style=3D"color: #660;" class=3D"styled-by-prettify">=
::</span><span style=3D"color: #000;" class=3D"styled-by-prettify">shared_p=
tr</span><span style=3D"color: #660;" class=3D"styled-by-prettify"><cons=
t </span><span style=3D"color: #606;" class=3D"styled-by-prettify">Master</=
span><span style=3D"color: #660;" class=3D"styled-by-prettify">></span><=
span style=3D"color: #000;" class=3D"styled-by-prettify"> </span><span styl=
e=3D"color: #606;" class=3D"styled-by-prettify">Slave</span><span style=3D"=
color: #660;" class=3D"styled-by-prettify">::</span><span style=3D"color: #=
000;" class=3D"styled-by-prettify">getMaster</span><span style=3D"color: #6=
60;" class=3D"styled-by-prettify">()</span><span style=3D"color: #000;" cla=
ss=3D"styled-by-prettify"> </span><span style=3D"color: #008;" class=3D"sty=
led-by-prettify">const</span><span style=3D"color: #000;" class=3D"styled-b=
y-prettify"><br></span><span style=3D"color: #660;" class=3D"styled-by-pret=
tify">{</span><span style=3D"color: #000;" class=3D"styled-by-prettify"><br=
>=C2=A0 auto master =3D std::get_underlying( m_master ); <br>=C2=A0 </span>=
<span style=3D"color: #008;" class=3D"styled-by-prettify">return</span><spa=
n style=3D"color: #000;" class=3D"styled-by-prettify"> std::const_pointer_c=
ast<const Master>( master );</span><span style=3D"color: #000;" class=
=3D"styled-by-prettify"><br></span><span style=3D"color: #660;" class=3D"st=
yled-by-prettify">}</span></div></code></div><p><br></p><p>For sure, I woul=
d never want an abomination like this to pass code review :)</p><p><br></p>=
<p>This little thought experiment showcases two major issues with get_under=
lying:</p><ul><li>Counter to the goal of of propagate_const, it makes it tr=
ivial to violate const-correctness without the compiler noticing (or, for t=
hat matter, any developer or static analysis tool that is unfamiliar with t=
he propagate_const API).<br></li><li>By not returning the const-correct typ=
e, get_underlying can make it unnecessarily hard to implement const-correct=
interfaces.<br></li></ul><p></p><p>The authors of the original propagate_c=
onst proposal were aware that the get_underlying interface was suboptimal, =
and for this reason they opted to make it less usable by hiding it as a fre=
e function instead of making it a proper method of propagate_const. But per=
sonnally, I would go further: drop get_underlying altogether, and replace i=
t with something that is both const-correct and respectful of pointer owner=
ship issues.<br></p><p><br></p><p>My counter-proposal would be to have a pr=
opagate_const method, maybe called "share()", which enables shari=
ng access to the data pointed by propagate_const with due respect paid to b=
oth const-correctness and the underlying pointer's ownership semantics.=
In practice:</p><ul><li>If called on propagate_const<T*>, share() sh=
ould return a T*</li><li>If called on const propagate_const<T*>, shar=
e() should return a const T*</li><li>If called on propagate_const<shared=
_ptr<T>>, share() should return a shared_ptr<T></li><li>If c=
alled on const propagate_const<shared_ptr<T>>, share() should r=
eturn a shared_ptr<const T></li><li>share() should not be defined on =
propagate_const<unique_ptr<T>> since that pointer does not supp=
ort sharing ownership</li></ul><p>In addition, as a convenience, whenever a=
share() method exists, an implicit cast could be provided to the underlyin=
g pointer type as a way to make such ownership sharing more convenient and =
to enable things like comparison of containers of shared_ptr<T> with =
containers of propagate_const<shared_ptr<T>> through standard (=
STL-ish) algorithms.</p><p><br></p><p>If you really, positively want someth=
ing like get_underlying to exist, it should only be applicable to non-const=
object. This alone would make it const-correct, and thus remove the need t=
o implement it as a free function and be careful when using it.</p><p><br><=
/p><p>With this simple change, shameless people who want to get their hands=
dirty and extract a mutable pointer from a const wrapper will then need to=
do some extra work, as should be expected of them in my opinion:</p><p><br=
></p><div style=3D"background-color: rgb(250, 250, 250); border-color: rgb(=
187, 187, 187); border-style: solid; border-width: 1px; overflow-wrap: brea=
k-word;" class=3D"prettyprint"><code class=3D"prettyprint"><div class=3D"su=
bprettyprint"><span style=3D"color: #000;" class=3D"styled-by-prettify"></s=
pan><span style=3D"color: #008;" class=3D"styled-by-prettify">using</span><=
span style=3D"color: #000;" class=3D"styled-by-prettify"> </span><span styl=
e=3D"color: #606;" class=3D"styled-by-prettify">ConstPropagator</span><span=
style=3D"color: #000;" class=3D"styled-by-prettify"> </span><span style=3D=
"color: #660;" class=3D"styled-by-prettify">=3D</span><span style=3D"color:=
#000;" class=3D"styled-by-prettify"> std</span><span style=3D"color: #660;=
" class=3D"styled-by-prettify">::</span><span style=3D"color: #000;" class=
=3D"styled-by-prettify">propagate_const</span><span style=3D"color: #660;" =
class=3D"styled-by-prettify"><</span><span style=3D"color: #000;" class=
=3D"styled-by-prettify">std</span><span style=3D"color: #660;" class=3D"sty=
led-by-prettify">::</span><span style=3D"color: #000;" class=3D"styled-by-p=
rettify">shared_ptr</span><span style=3D"color: #660;" class=3D"styled-by-p=
rettify"><</span><span style=3D"color: #000;" class=3D"styled-by-prettif=
y">T</span><span style=3D"color: #660;" class=3D"styled-by-prettify">>&g=
t;;</span><span style=3D"color: #000;" class=3D"styled-by-prettify"><br><br=
></span><span style=3D"color: #008;" class=3D"styled-by-prettify">const</sp=
an><span style=3D"color: #000;" class=3D"styled-by-prettify"> </span><span =
style=3D"color: #606;" class=3D"styled-by-prettify">ConstPropagator</span><=
span style=3D"color: #660;" class=3D"styled-by-prettify">&</span><span =
style=3D"color: #000;" class=3D"styled-by-prettify"> immutableRef </span><s=
pan style=3D"color: #660;" class=3D"styled-by-prettify">=3D</span><span sty=
le=3D"color: #000;" class=3D"styled-by-prettify"> </span><span style=3D"col=
or: #660;" class=3D"styled-by-prettify">...</span><span style=3D"color: #00=
0;" class=3D"styled-by-prettify"> </span><span style=3D"color: #660;" class=
=3D"styled-by-prettify">;</span><span style=3D"color: #000;" class=3D"style=
d-by-prettify"><br>std</span><span style=3D"color: #660;" class=3D"styled-b=
y-prettify">::</span><span style=3D"color: #000;" class=3D"styled-by-pretti=
fy">shared_ptr</span><span style=3D"color: #660;" class=3D"styled-by-pretti=
fy"><</span><span style=3D"color: #000;" class=3D"styled-by-prettify">T<=
/span><span style=3D"color: #660;" class=3D"styled-by-prettify">></span>=
<span style=3D"color: #000;" class=3D"styled-by-prettify"> mutableRef </spa=
n><span style=3D"color: #660;" class=3D"styled-by-prettify">=3D</span><span=
style=3D"color: #000;" class=3D"styled-by-prettify"> </span><span style=3D=
"color: #008;" class=3D"styled-by-prettify">const_cast</span><span style=3D=
"color: #660;" class=3D"styled-by-prettify"><</span><span style=3D"color=
: #606;" class=3D"styled-by-prettify">ConstPropagator</span><span style=3D"=
color: #660;" class=3D"styled-by-prettify">*>(</span><span style=3D"colo=
r: #000;" class=3D"styled-by-prettify"> </span><span style=3D"color: #660;"=
class=3D"styled-by-prettify">&</span><span style=3D"color: #000;" clas=
s=3D"styled-by-prettify">immutableRef </span><span style=3D"color: #660;" c=
lass=3D"styled-by-prettify">)-></span><span style=3D"color: #000;" class=
=3D"styled-by-prettify">get_underlying</span><span style=3D"color: #660;" c=
lass=3D"styled-by-prettify">();</span></div></code></div></div>
<p></p>
-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/9ad3f22a-a619-4488-bf1e-303641c968b9%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/9ad3f22a-a619-4488-bf1e-303641c968b9=
%40isocpp.org</a>.<br />
------=_Part_745_161024322.1487688895299--
------=_Part_744_862312118.1487688895297--
.
Author: Arthur O'Dwyer <arthur.j.odwyer@gmail.com>
Date: Tue, 21 Feb 2017 18:14:44 -0800 (PST)
Raw View
------=_Part_713_690374026.1487729684916
Content-Type: multipart/alternative;
boundary="----=_Part_714_1604873098.1487729684917"
------=_Part_714_1604873098.1487729684917
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
On Tuesday, February 21, 2017 at 6:54:55 AM UTC-8, Hadrien Grasland wrote:
>
> Hi everyone,
>
> I am not sure what this group's policy is regarding resurrection of old=
=20
> threads, so I have decided to create a new one. But I would like to secon=
d=20
> two comments that were previously made on the propagate_const proposal of=
=20
> N4617:
>
> 1/ That it should be possible to copy a propagate_const<T> wrapper (=20
> https://groups.google.com/a/isocpp.org/forum/#!searchin/std-proposals/pro=
pagate_const/std-proposals/1uDKcA9bssU/AZ-aQNAZAAAJ=20
> ).
> 2/ That propagate_const<T> should be convertible to a const-correct form=
=20
> of the underlying pointer type (=20
> https://groups.google.com/a/isocpp.org/forum/#!searchin/std-proposals/pro=
pagate_const/std-proposals/7rSMtvQVASk/niLSKtkcBwAJ=20
> ).
>
> Let me motivate, through some concrete use cases, why I think that both o=
f=20
> these changes are necessary.
>
>
> Need for shallow copy operations
>
> There are many reasons to use pointers in C++. A very common use case,=20
> however, is dynamic polymorphism:
>
> - Different implementations of a virtual interface may have different=
=20
> sizes
> - C++, in its commendable quest for zero-cost abstraction, does not=20
> provide native support for dynamically sized types
> - When combined, these two language design choices lead to the=20
> conclusion that if the set of implementations of an interface is not k=
nown=20
> as compile time, polymorphism intrinsically requires pointer indirecti=
on in=20
> C++
>
> For this use case, pointer constness semantics are inadequate. If you use=
=20
> a pointer-to-const, you cannot modify the target object, which is often=
=20
> fine but tends to be excessively limitating for some use cases,=20
> particularly when implementing constructors and factory functions. If, on=
=20
> the other hand, you use a pointer-to-mutable, you open a const-correctnes=
s=20
> hole by allowing yourself to mutate a const object without any compiler=
=20
> warning. Quite frankly, none of these options are very appealing.
>
> // Pointer-to-const restricts some valid use cases of polymorphism
> class FirstTry
> {
> private:
> std::unique_ptr<const Interface> m_ptr;
>
> public:
> FirstTry()
> : m_ptr{ std::make_unique<Implementation>( ... ) }
> {
> m_ptr->setParent( *this ); // ERROR: Can't mutate through a const=20
> pointer!
> }
> };
>
>
> // Pointer-to-mutable breaks const correctness of polymorphism
> class SecondTry
> {
> private:
> std::unique_ptr<Interface> m_ptr;
>
> public:
> SecondTry()
> : m_ptr{ std::make_unique<Implementation>( ... ) }
> {
> m_ptr->setParent( *this ); // This is now okay...
> }
>
> int someRandomAccessor() const {
> m_ptr->setSomething(); // ...but unfortunately, this is okay too :-(
> return 42;
> }
> };
>
> Now, if we are on the same wavelength, and I will assume in the following=
=20
> that this is the case, this is precisely the kind of issue that=20
> propagate_const is designed to help with.
>
> Let us now turn our attention to copy operations. Ideally, polymorphic=20
> objects should be deep-copyable, just like any regular value type.=20
> Unfortunately, in another application of the zero cost abstraction=20
> principle, C++ provides no easy way to do this. Anyone who wants to combi=
ne=20
> value semantics and dynamic polymorphism needs to provide explicit suppor=
t=20
> for deep copies across the entire class hierarchy, which is cumbersome to=
=20
> begin with and can get problematic when interacting with third-party=20
> libraries.
>
> class Interface
> {
> public:
> // This kind of boilerplate must be added to every class supporting=20
> deep copy...
> virtual std::unique_ptr<Interface> clone() const =3D 0;
> };
>
> class Implementation : public Interface
> {
> public:
> // ...and replicated again and again in every single non-abstract=20
> child. No fun.
> std::unique_ptr<Interface> clone() const final override
> {
> return std::make_unique<Implementation>( *this );
> }
> };
>
> But thankfully, sometimes, we can do without a true deep copy, and satisf=
y=20
> ourselves with a shallow copy. And in fact, for some use cases, a shallow=
=20
> copy will even be exactly what we want:
>
> - When building some variety of search acceleration structure=20
> (hashmap, neighbour locator, axis-aligned-bounding box...)
> - When many "slave objects" share a reference to some common,=20
> potentially large "master object"
>
> Unfortunately, even though all standard C++ pointer types make it trivial=
=20
> to shallow-copy a polymorphic object, propagate_const gets in our way her=
e=20
> by being move only. I see this design choice as a shortcoming, because it=
=20
> reduces the usefulness of propagate_const in many dynamic polymorphism us=
e=20
> cases where the underlying pointer type would have done just fine. For=20
> example, without shallow copy operations...
>
> - You cannot let the compiler implement a shallow copy constructor for=
=20
> you using "=3D default"
> - You cannot use std::copy_if to find objects matching some predicate=
=20
> in an internal dataset
> - You cannot easily build object search acceleration structures, such=
=20
> as hashmaps or AABBs
> - You cannot easily share a reference to an object wrapped by=20
> propagate_const with other objects
>
> I am aware that shallow copies are possible using the get() and=20
> get_underlying() operations. I do not see this as a satisfactory answer,=
=20
> because it breaks every STL-ish algorithm that expects a copy constructor=
..=20
> Of course, I could build a propagate_const wrapper that has a copy=20
> constructor myself, and this is what I would end up doing if=20
> propagate_const were accepted in the STL in its current form. But I think=
=20
> that the use cases that I mentioned above are valid enough to warrant=20
> changing the design of propagate_const instead.
>
>
> I am aware that in order to be const-correct, a shallow copy constructor=
=20
> for propagate_const would need to operate from a non-const reference. I a=
m=20
> fine with this tradeoff. The main reason we usually allow ourselves to ma=
ke=20
> mutable copies from const objects is because we assume the copy to be=20
> independent from the original object. This assumption is broken when maki=
ng=20
> shallow copies through pointer or reference types. Sure, many APIs will b=
e=20
> broken initially, but that is unavoidable when fixing old programming=20
> language flaws. Bugs will be reported, and interfaces will be fixed, the=
=20
> way it's always been done.
>
>
> For prior art, if you look at the STL's documentation, you will find that=
=20
> customizable algorithms that copy const data, such as std::copy_if,=20
> explicitly do NOT require the user-provided functions to consume the data=
=20
> by const-reference. So the idea of copying from non-const is not new. The=
=20
> C++ community only needs to re-discover it.
>
>
> I am also aware of the objection, made in a thread linked above, that "We=
=20
> were not confident that a non-const copy constructor would protect the us=
er=20
> from accidental loss of const and accidental mutable shared state". As fa=
r=20
> as I'm concerned, this hand-waving statement was not properly justified.=
=20
> From my perspective, the proposed alternative of using get_underlying see=
ms=20
> to be much bigger breach of const-correctness than a copy constructor tha=
t=20
> operates from mutable, because from a semantic point of view,=20
> get_underlying is essentially a silent const_cast that standard code=20
> analysis tools won't catch.
>
>
>
> Const-correct convertibility to the underlying pointer type
>
> Sometimes, one has to extract a pointer back from the propagate_const=20
> wrapper. I would see two main use cases for this:
>
> - To feed a legacy API that is not (yet?) compatible with=20
> propagate_const.
> - To turn a propagate_const<T> into a pointer-to-const, when the=20
> recipient should not be able to modify to the target object
>
> The current propagate_const interface provides three ways to do this:
>
> - Call get() and get a raw pointer
> - Rely on an implicit cast to raw pointer, if available
> - Use get_underlying to access the internals of propagate_const.
>
> As I'm going to elaborate, the first two operations are not always the=20
> right tool for the job at hand, because they fail to convey important=20
> ownership information from the underlying pointer type. Whereas=20
> get_underlying, as currently implemented, is a flawed interface that goes=
=20
> directly against the design goals of propagate_const and should be=20
> eradicated before standardization.
>
>
> As a use case, suppose that as discussed previously, I am working a "slav=
e=20
> object", which holds a shared_ptr to an associated "master object" that i=
t=20
> shares with other slaves. To improve the const-correctness of this design=
,=20
> I have decided to refactor the shared_ptr into a=20
> propagate_const<shared_ptr>. So far, so good.
>
>
> class Master
> {
> ...
> };
>
> class Slave
> {
> private:
> // This was refactored from "std::shared_ptr<Master> m_master"
> std::propagate_const<std::shared_ptr<Master>> m_master;
>
> public:
> Slave( std::shared_ptr<Master> master )
> : m_master(master)
> {
> m_master->addSlave( *this );
> }
>
> ...
> };
>
> But as I proceed with the refactoring, I discover that the Slave class=20
> used to provide a method that shares access to its master:
>
> std::shared_ptr<Master> Slave::getMaster() const
> {
> return m_master;
> }
>
> This does not compile anymore. And I'm happy about that: it should never=
=20
> have compiled to begin with. Returning non-const access to my members fro=
m=20
> a const method definitely does not match my idea of const-correctness!
>
> Instead, I would like to only provide const access to the master object,=
=20
> like so:
>
> std::shared_ptr<const Master> Slave::getMaster() const;
>
> If my clients are well-behaved and do not mutate the master object, this=
=20
> will be a minimally invasive interface change. It will require only minor=
=20
> client rewrites, the kind that could be automated by sed or an IDE. It=20
> could even require no client rewrite at all if I end up being lucky and=
=20
> have clients that were using auto and friends.
>
> Unfortunately, the current propagate_const interface does not allow me to=
=20
> implement this method in a clean way.
>
> The obvious code snippet would not work due to the lack of an appropriate=
=20
> implicit conversion:
>
> std::shared_ptr<const Master> Slave::getMaster() const
> {
> return m_master; // ERROR: No implicit conversion from=20
> propagate_const<shared_ptr>!
> }
>
> Using get() here would be the perfect way to introduce use after free bug=
s:
>
> std::shared_ptr<const Master> Slave::getMaster() const
> {
> const Master* master =3D m_master.get();
> return std::shared_ptr<Master>( master ); // INCORRECT: Two owners for=
=20
> one object!
> }
>
> And using get_underlying will both break const correctness and fail to=20
> provide me with the correct return type:
>
> std::shared_ptr<const Master> Slave::getMaster() const
> {
> auto master =3D std::get_underlying( m_master ); // This is an=20
> std::shared_ptr<Master>
> return master; // ERROR: No implicit conversion to=20
> std::shared_ptr<const Master>!
> }
>
> To implement this method, in addition to the const-incorrect=20
> get_underlying interface, I would also need a const cast!
>
> std::shared_ptr<const Master> Slave::getMaster() const
> {
> auto master =3D std::get_underlying( m_master );=20
> return std::const_pointer_cast<const Master>( master );
> }
>
>
> For sure, I would never want an abomination like this to pass code review=
=20
> :)
>
>
> This little thought experiment showcases two major issues with=20
> get_underlying:
>
> - Counter to the goal of of propagate_const, it makes it trivial to=20
> violate const-correctness without the compiler noticing (or, for that=
=20
> matter, any developer or static analysis tool that is unfamiliar with =
the=20
> propagate_const API).
> - By not returning the const-correct type, get_underlying can make it=
=20
> unnecessarily hard to implement const-correct interfaces.
> =20
> The authors of the original propagate_const proposal were aware that the=
=20
> get_underlying interface was suboptimal, and for this reason they opted t=
o=20
> make it less usable by hiding it as a free function instead of making it =
a=20
> proper method of propagate_const. But personnally, I would go further: dr=
op=20
> get_underlying altogether, and replace it with something that is both=20
> const-correct and respectful of pointer ownership issues.
>
>
> My counter-proposal would be to have a propagate_const method, maybe=20
> called "share()", which enables sharing access to the data pointed by=20
> propagate_const with due respect paid to both const-correctness and the=
=20
> underlying pointer's ownership semantics. In practice:
>
> - If called on propagate_const<T*>, share() should return a T*
> - If called on const propagate_const<T*>, share() should return a=20
> const T*
> - If called on propagate_const<shared_ptr<T>>, share() should return a=
=20
> shared_ptr<T>
> - If called on const propagate_const<shared_ptr<T>>, share() should=20
> return a shared_ptr<const T>
> - share() should not be defined on propagate_const<unique_ptr<T>>=20
> since that pointer does not support sharing ownership
>
> This all seems great, but...
"const propagate_const<shared_ptr<T>>, share() should return a=20
shared_ptr<const T>"
How do you envision this being specified? Remember that propagate_const is=
=20
just a template. So either you have to come up with generic semantics that=
=20
produce this result "by happy accident", or else you have to decide that=20
propagate_const is "built-in magic" and only works for std::shared_ptr (and=
=20
*not* boost::shared_ptr<T> and *not* dropbox::nn<T*> and so on), or else=20
you have to wade into the customization-point swamp. Which route would you=
=20
like to take?
=E2=80=93Arthur
P.S.: I do think that propagate_const<T>::get()=20
<http://en.cppreference.com/w/cpp/experimental/propagate_const/get> is=20
currently mis-specified; it's proposed to return t_.get() when the actually=
=20
useful value to return would be t_. But we already had a thread about that.=
=20
;)
--=20
You received this message because you are subscribed to the Google Groups "=
ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp=
..org/d/msgid/std-proposals/519ae870-041a-4197-aa9f-90ed82aa26f3%40isocpp.or=
g.
------=_Part_714_1604873098.1487729684917
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">On Tuesday, February 21, 2017 at 6:54:55 AM UTC-8, Hadrien=
Grasland wrote:<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin=
-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"lt=
r">Hi everyone,<br><br>I am not sure what this group's policy is regard=
ing resurrection of old threads, so I have decided to create a new one. But=
I would like to second two comments that were previously made on the propa=
gate_const proposal of N4617:<br><br>1/ That it should be possible to copy =
a propagate_const<T> wrapper ( <a href=3D"https://groups.google.com/a=
/isocpp.org/forum/#!searchin/std-proposals/propagate_const/std-proposals/1u=
DKcA9bssU/AZ-aQNAZAAAJ" target=3D"_blank" rel=3D"nofollow" onmousedown=3D"t=
his.href=3D'https://groups.google.com/a/isocpp.org/forum/#!searchin/std=
-proposals/propagate_const/std-proposals/1uDKcA9bssU/AZ-aQNAZAAAJ';retu=
rn true;" onclick=3D"this.href=3D'https://groups.google.com/a/isocpp.or=
g/forum/#!searchin/std-proposals/propagate_const/std-proposals/1uDKcA9bssU/=
AZ-aQNAZAAAJ';return true;">https://groups.google.com/a/<wbr>isocpp.org=
/forum/#!searchin/<wbr>std-proposals/propagate_const/<wbr>std-proposals/1uD=
KcA9bssU/AZ-<wbr>aQNAZAAAJ</a> ).<br>2/ That propagate_const<T> shoul=
d be convertible to a const-correct form of the underlying pointer type ( <=
a href=3D"https://groups.google.com/a/isocpp.org/forum/#!searchin/std-propo=
sals/propagate_const/std-proposals/7rSMtvQVASk/niLSKtkcBwAJ" target=3D"_bla=
nk" rel=3D"nofollow" onmousedown=3D"this.href=3D'https://groups.google.=
com/a/isocpp.org/forum/#!searchin/std-proposals/propagate_const/std-proposa=
ls/7rSMtvQVASk/niLSKtkcBwAJ';return true;" onclick=3D"this.href=3D'=
https://groups.google.com/a/isocpp.org/forum/#!searchin/std-proposals/propa=
gate_const/std-proposals/7rSMtvQVASk/niLSKtkcBwAJ';return true;">https:=
//groups.google.com/a/<wbr>isocpp.org/forum/#!searchin/<wbr>std-proposals/p=
ropagate_const/<wbr>std-proposals/7rSMtvQVASk/<wbr>niLSKtkcBwAJ</a> ).<br><=
br>Let me motivate, through some concrete use cases, why I think that both =
of these changes are necessary.<br><br><br><font size=3D"4">Need for shallo=
w copy operations</font><br><br>There are many reasons to use pointers in C=
++. A very common use case, however, is dynamic polymorphism:<br><ul><li>Di=
fferent implementations of a virtual interface may have different sizes</li=
><li>C++, in its commendable quest for zero-cost abstraction, does not prov=
ide native support for dynamically sized types</li><li>When combined, these=
two language design choices lead to the conclusion that if the set of impl=
ementations of an interface is not known as compile time, polymorphism intr=
insically requires pointer indirection in C++</li></ul>For this use case, p=
ointer constness semantics are inadequate. If you use a pointer-to-const, y=
ou cannot modify the target object, which is often fine but tends to be exc=
essively limitating for some use cases, particularly when implementing cons=
tructors and factory functions. If, on the other hand, you use a pointer-to=
-mutable, you open a const-correctness hole by allowing yourself to mutate =
a const object without any compiler warning. Quite frankly, none of these o=
ptions are very appealing.<br><br><div style=3D"background-color:rgb(250,25=
0,250);border-color:rgb(187,187,187);border-style:solid;border-width:1px"><=
code><div><span style=3D"color:#800">// Pointer-to-const restricts some val=
id use cases of polymorphism</span><span style=3D"color:#000"><br></span><s=
pan style=3D"color:#008">class</span><span style=3D"color:#000"> </span><sp=
an style=3D"color:#606">FirstTry</span><span style=3D"color:#000"><br></spa=
n><span style=3D"color:#660">{</span><span style=3D"color:#000"></span><br>=
<code><span style=3D"color:#008">private</span><span style=3D"color:#660">:=
</span><span style=3D"color:#000"><br>=C2=A0 std</span><span style=3D"color=
:#660">::</span><span style=3D"color:#000">unique_ptr</span><span style=3D"=
color:#660"><</span><span style=3D"color:#008">const</span><span style=
=3D"color:#000"> </span><span style=3D"color:#606">Interface</span><span st=
yle=3D"color:#660">></span><span style=3D"color:#000"> m_ptr</span><span=
style=3D"color:#660">;</span><span style=3D"color:#000"><br><br></span></c=
ode><span style=3D"color:#008">public</span><span style=3D"color:#660">:</s=
pan><span style=3D"color:#000"><br>=C2=A0 </span><span style=3D"color:#606"=
>FirstTry</span><span style=3D"color:#660">()</span><span style=3D"color:#0=
00"><br>=C2=A0 =C2=A0 </span><span style=3D"color:#660">:</span><span style=
=3D"color:#000"> m_ptr</span><span style=3D"color:#660">{</span><span style=
=3D"color:#000"> std</span><span style=3D"color:#660">::</span><span style=
=3D"color:#000">make_unique</span><span style=3D"color:#660"><</span><sp=
an style=3D"color:#606">Implementatio<wbr>n</span><span style=3D"color:#660=
">>(</span><span style=3D"color:#000"> </span><span style=3D"color:#660"=
>...</span><span style=3D"color:#000"> </span><span style=3D"color:#660">)<=
/span><span style=3D"color:#000"> </span><span style=3D"color:#660">}</span=
><span style=3D"color:#000"><br>=C2=A0 </span><span style=3D"color:#660">{<=
/span><span style=3D"color:#000"><br>=C2=A0 =C2=A0 m_ptr</span><span style=
=3D"color:#660">-></span><span style=3D"color:#000">setParent</span><spa=
n style=3D"color:#660">(</span><span style=3D"color:#000"> </span><span sty=
le=3D"color:#660">*</span><span style=3D"color:#008">this</span><span style=
=3D"color:#000"> </span><span style=3D"color:#660">);</span><span style=3D"=
color:#000"> =C2=A0</span><span style=3D"color:#800">// ERROR:</span><span =
style=3D"color:#000"> Can't mutate through a const pointer!<br>=C2=A0 <=
/span><span style=3D"color:#660">}</span><span style=3D"color:#000"><br></s=
pan><span style=3D"color:#660">};</span><span style=3D"color:#000"><br><br>=
<br></span><span style=3D"color:#800">// Pointer-to-mutable breaks const co=
rrectness of polymorphism</span><span style=3D"color:#000"><br></span><span=
style=3D"color:#008">class</span><span style=3D"color:#000"> </span><span =
style=3D"color:#606">SecondTry</span><span style=3D"color:#000"><br></span>=
<span style=3D"color:#660">{</span><span style=3D"color:#000"><br>private:<=
br>=C2=A0 std::unique_ptr<Interface> m_ptr;<br><br></span><span style=
=3D"color:#008">public</span><span style=3D"color:#660">:</span><span style=
=3D"color:#000"><br>=C2=A0 </span><span style=3D"color:#606">SecondTry</spa=
n><span style=3D"color:#660">()</span><span style=3D"color:#000"><br>=C2=A0=
=C2=A0 </span><span style=3D"color:#660">:</span><span style=3D"color:#000=
"> m_ptr</span><span style=3D"color:#660">{</span><span style=3D"color:#000=
"> std</span><span style=3D"color:#660">::</span><span style=3D"color:#000"=
>make_unique</span><span style=3D"color:#660"><</span><span style=3D"col=
or:#606">Implementatio<wbr>n</span><span style=3D"color:#660">>(</span><=
span style=3D"color:#000"> </span><span style=3D"color:#660">...</span><spa=
n style=3D"color:#000"> </span><span style=3D"color:#660">)</span><span sty=
le=3D"color:#000"> </span><span style=3D"color:#660">}</span><span style=3D=
"color:#000"><br>=C2=A0 </span><span style=3D"color:#660">{</span><span sty=
le=3D"color:#000"><br>=C2=A0 =C2=A0 m_ptr</span><span style=3D"color:#660">=
-></span><span style=3D"color:#000">setParent</span><span style=3D"color=
:#660">(</span><span style=3D"color:#000"> </span><span style=3D"color:#660=
">*</span><span style=3D"color:#008">this</span><span style=3D"color:#000">=
</span><span style=3D"color:#660">);</span><span style=3D"color:#000"> =C2=
=A0</span><span style=3D"color:#800">// This is now okay...</span><span sty=
le=3D"color:#000"><br>=C2=A0 </span><span style=3D"color:#660">}</span><spa=
n style=3D"color:#000"><br><br>=C2=A0 </span><span style=3D"color:#008">int=
</span><span style=3D"color:#000"> someRandomAccessor</span><span style=3D"=
color:#660">()</span><span style=3D"color:#000"> </span><span style=3D"colo=
r:#008">const</span><span style=3D"color:#000"> </span><span style=3D"color=
:#660">{</span><span style=3D"color:#000"><br>=C2=A0 =C2=A0 m_ptr</span><sp=
an style=3D"color:#660">-></span><span style=3D"color:#000">setSomething=
</span><span style=3D"color:#660">();</span><span style=3D"color:#000"> =C2=
=A0</span><span style=3D"color:#800">// ...but unfortunately, this is okay =
too :-(</span><span style=3D"color:#000"><br>=C2=A0 =C2=A0 </span><span sty=
le=3D"color:#008">return</span><span style=3D"color:#000"> </span><span sty=
le=3D"color:#066">42</span><span style=3D"color:#660">;</span><span style=
=3D"color:#000"><br>=C2=A0 </span><span style=3D"color:#660">}</span><span =
style=3D"color:#000"><br>};</span><span style=3D"color:#000"><br></span></d=
iv></code></div><br>Now, if we are on the same wavelength, and I will assum=
e in the following that this is the case, this is precisely the kind of iss=
ue that propagate_const is designed to help with.<br><br>Let us now turn ou=
r attention to copy operations. Ideally, polymorphic objects should be deep=
-copyable, just like any regular value type. Unfortunately, in another appl=
ication of the zero cost abstraction principle, C++ provides no easy way to=
do this. Anyone who wants to combine value semantics and dynamic polymorph=
ism needs to provide explicit support for deep copies across the entire cla=
ss hierarchy, which is cumbersome to begin with and can get problematic whe=
n interacting with third-party libraries.<br><br><div style=3D"background-c=
olor:rgb(250,250,250);border-color:rgb(187,187,187);border-style:solid;bord=
er-width:1px"><code><div><span style=3D"color:#008">class</span><span style=
=3D"color:#000"> </span><span style=3D"color:#606">Interface</span><span st=
yle=3D"color:#000"><br></span><span style=3D"color:#660">{</span><span styl=
e=3D"color:#000"><br></span><span style=3D"color:#008">public</span><span s=
tyle=3D"color:#660">:</span><span style=3D"color:#000"><br>=C2=A0 </span><s=
pan style=3D"color:#800">// This kind of boilerplate must be added to every=
class supporting deep copy...</span><span style=3D"color:#000"><br>=C2=A0 =
</span><span style=3D"color:#008">virtual</span><span style=3D"color:#000">=
std</span><span style=3D"color:#660">::</span><span style=3D"color:#000">u=
nique_ptr</span><span style=3D"color:#660"><</span><span style=3D"color:=
#606">Interface</span><span style=3D"color:#660">></span><span style=3D"=
color:#000"> clone</span><span style=3D"color:#660">()</span><span style=3D=
"color:#000"> </span><span style=3D"color:#008">const</span><span style=3D"=
color:#000"> </span><span style=3D"color:#660">=3D</span><span style=3D"col=
or:#000"> </span><span style=3D"color:#066">0</span><span style=3D"color:#6=
60">;</span><span style=3D"color:#000"><br></span><span style=3D"color:#660=
">};</span><span style=3D"color:#000"><br><br></span><span style=3D"color:#=
008">class</span><span style=3D"color:#000"> </span><span style=3D"color:#6=
06">Implementation</span><span style=3D"color:#000"> </span><span style=3D"=
color:#660">:</span><span style=3D"color:#000"> </span><span style=3D"color=
:#008">public</span><span style=3D"color:#000"> </span><span style=3D"color=
:#606">Interface</span><span style=3D"color:#000"><br></span><span style=3D=
"color:#660">{</span><span style=3D"color:#000"><br></span><span style=3D"c=
olor:#008">public</span><span style=3D"color:#660">:</span><span style=3D"c=
olor:#000"><br>=C2=A0 </span><span style=3D"color:#800">// ...and replicate=
d again and again in every single non-abstract child. No fun.</span><span s=
tyle=3D"color:#000"><br>=C2=A0 std</span><span style=3D"color:#660">::</spa=
n><span style=3D"color:#000">unique_ptr</span><span style=3D"color:#660">&l=
t;</span><span style=3D"color:#606">Interface</span><span style=3D"color:#6=
60">></span><span style=3D"color:#000"> clone</span><span style=3D"color=
:#660">()</span><span style=3D"color:#000"> </span><span style=3D"color:#00=
8">const</span><span style=3D"color:#000"> </span><span style=3D"color:#008=
">final</span><span style=3D"color:#000"> </span><span style=3D"color:#008"=
>override</span><span style=3D"color:#000"><br>=C2=A0 </span><span style=3D=
"color:#660">{</span><span style=3D"color:#000"><br>=C2=A0 =C2=A0 </span><s=
pan style=3D"color:#008">return</span><span style=3D"color:#000"> std</span=
><span style=3D"color:#660">::</span><span style=3D"color:#000">make_unique=
</span><span style=3D"color:#660"><</span><span style=3D"color:#606">Imp=
lementatio<wbr>n</span><span style=3D"color:#660">>(</span><span style=
=3D"color:#000"> </span><span style=3D"color:#660">*</span><span style=3D"c=
olor:#008">this</span><span style=3D"color:#000"> </span><span style=3D"col=
or:#660">);</span><span style=3D"color:#000"><br>=C2=A0 </span><span style=
=3D"color:#660">}</span><span style=3D"color:#000"><br></span><span style=
=3D"color:#660">};</span></div></code></div><br>But thankfully, sometimes, =
we can do without a true deep copy, and satisfy ourselves with a shallow co=
py. And in fact, for some use cases, a shallow copy will even be exactly wh=
at we want:<br><ul><li>When building some variety of search acceleration st=
ructure (hashmap, neighbour locator, axis-aligned-bounding box...)</li><li>=
When many "slave objects" share a reference to some common, poten=
tially large "master object"</li></ul>Unfortunately, even though =
all standard C++ pointer types make it trivial to shallow-copy a polymorphi=
c object, propagate_const gets in our way here by being move only. I see th=
is design choice as a shortcoming, because it reduces the usefulness of pro=
pagate_const in many dynamic polymorphism use cases where the underlying po=
inter type would have done just fine. For example, without shallow copy ope=
rations...<br><ul><li>You cannot let the compiler implement a shallow copy =
constructor for you using "=3D default"<br></li><li>You cannot us=
e std::copy_if to find objects matching some predicate in an internal datas=
et</li><li>You cannot easily build object search acceleration structures, s=
uch as hashmaps or AABBs</li><li>You cannot easily share a reference to an =
object wrapped by propagate_const with other objects</li></ul><p>I am aware=
that shallow copies are possible using the get() and get_underlying() oper=
ations. I do not see this as a satisfactory answer, because it breaks every=
STL-ish algorithm that expects a copy constructor. Of course, I could buil=
d a propagate_const wrapper that has a copy constructor myself, and this is=
what I would end up doing if propagate_const were accepted in the STL in i=
ts current form. But I think that the use cases that I mentioned above are =
valid enough to warrant changing the design of propagate_const instead.</p>=
<p><br></p><p>I am aware that in order to be const-correct, a shallow copy =
constructor for propagate_const would need to operate from a non-const refe=
rence. I am fine with this tradeoff. The main reason we usually allow ourse=
lves to make mutable copies from const objects is because we assume the cop=
y to be independent from the original object. This assumption is broken whe=
n making shallow copies through pointer or reference types. Sure, many APIs=
will be broken initially, but that is unavoidable when fixing old programm=
ing language flaws. Bugs will be reported, and interfaces will be fixed, th=
e way it's always been done.<br></p><p><br></p><p>For prior art, if you=
look at the STL's documentation, you will find that customizable algor=
ithms that copy const data, such as std::copy_if, explicitly do NOT require=
the user-provided functions to consume the data by const-reference. So the=
idea of copying from non-const is not new. The C++ community only needs to=
re-discover it.<br></p><p><br></p><p>I am also aware of the objection, mad=
e in a thread linked above, that "We were not confident that a non-con=
st copy constructor would protect=20
the user from accidental loss of const and accidental mutable shared=20
state". As far as I'm concerned, this hand-waving statement was no=
t properly justified. From my perspective, the proposed alternative of usin=
g get_underlying seems to be much bigger breach of const-correctness than a=
copy constructor that operates from mutable, because from a semantic point=
of view, get_underlying is essentially a silent const_cast that standard c=
ode analysis tools won't catch.</p><p><br></p><br><font size=3D"4">Cons=
t-correct convertibility to the underlying pointer type</font><br><br>Somet=
imes, one has to extract a pointer back from the propagate_const wrapper. I=
would see two main use cases for this:<br><ul><li>To feed a legacy API tha=
t is not (yet?) compatible with propagate_const.<br></li><li>To turn a prop=
agate_const<T> into a pointer-to-const, when the recipient should not=
be able to modify to the target object</li></ul><p>The current propagate_c=
onst interface provides three ways to do this:</p><ul><li>Call get() and ge=
t a raw pointer</li><li>Rely on an implicit cast to raw pointer, if availab=
le</li><li>Use get_underlying to access the internals of propagate_const.</=
li></ul><p>As I'm going to elaborate, the first two operations are not =
always the right tool for the job at hand, because they fail to convey impo=
rtant ownership information from the underlying pointer type. Whereas get_u=
nderlying, as currently implemented, is a flawed interface that goes direct=
ly against the design goals of propagate_const and should be eradicated bef=
ore standardization.</p><p><br></p><p>As a use case, suppose that as discus=
sed previously, I am working a "slave object", which holds a shar=
ed_ptr to an associated "master object" that it shares with other=
slaves. To improve the const-correctness of this design, I have decided to=
refactor the shared_ptr into a propagate_const<shared_ptr>. So far, =
so good.<br></p><p><br></p><p></p><div style=3D"background-color:rgb(250,25=
0,250);border-color:rgb(187,187,187);border-style:solid;border-width:1px"><=
code><div><span style=3D"color:#008">class</span><span style=3D"color:#000"=
> </span><span style=3D"color:#606">Master</span><span style=3D"color:#000"=
><br></span><span style=3D"color:#660">{</span><span style=3D"color:#000"><=
br>=C2=A0 </span><span style=3D"color:#660">...</span><span style=3D"color:=
#000"><br></span><span style=3D"color:#660">};</span><span style=3D"color:#=
000"><br><br></span><span style=3D"color:#008">class</span><span style=3D"c=
olor:#000"> </span><span style=3D"color:#606">Slave</span><span style=3D"co=
lor:#000"><br></span><span style=3D"color:#660">{</span><span style=3D"colo=
r:#000"><br></span><span style=3D"color:#008">private</span><span style=3D"=
color:#660">:</span><span style=3D"color:#000"><br>=C2=A0 </span><span styl=
e=3D"color:#800">// This was refactored from "std::shared_ptr<Maste=
r> m_master"</span><span style=3D"color:#000"><br>=C2=A0 std</span>=
<span style=3D"color:#660">::</span><span style=3D"color:#000">propagate_co=
nst</span><span style=3D"color:#660"><</span><span style=3D"color:#000">=
std</span><span style=3D"color:#660">::</span><span style=3D"color:#000">sh=
ar<wbr>ed_ptr</span><span style=3D"color:#660"><</span><span style=3D"co=
lor:#606">Master</span><span style=3D"color:#660">>></span><span styl=
e=3D"color:#000"> m_master</span><span style=3D"color:#660">;</span><span s=
tyle=3D"color:#000"><br><br></span><span style=3D"color:#008">public</span>=
<span style=3D"color:#660">:</span><span style=3D"color:#000"><br>=C2=A0 </=
span><span style=3D"color:#606">Slave</span><span style=3D"color:#660">(</s=
pan><span style=3D"color:#000"> std</span><span style=3D"color:#660">::</sp=
an><span style=3D"color:#000">shared_ptr</span><span style=3D"color:#660">&=
lt;</span><span style=3D"color:#606">Master</span><span style=3D"color:#660=
">></span><span style=3D"color:#000"> master </span><span style=3D"color=
:#660">)</span><span style=3D"color:#000"><br>=C2=A0 =C2=A0 </span><span st=
yle=3D"color:#660">:</span><span style=3D"color:#000"> m_master</span><span=
style=3D"color:#660">(</span><span style=3D"color:#000">master</span><span=
style=3D"color:#660">)</span><span style=3D"color:#000"><br>=C2=A0 </span>=
<span style=3D"color:#660">{</span><span style=3D"color:#000"><br>=C2=A0 =
=C2=A0 m_master</span><span style=3D"color:#660">-></span><span style=3D=
"color:#000">addSlave</span><span style=3D"color:#660">(</span><span style=
=3D"color:#000"> </span><span style=3D"color:#660">*</span><span style=3D"c=
olor:#008">this</span><span style=3D"color:#000"> </span><span style=3D"col=
or:#660">);</span><span style=3D"color:#000"><br>=C2=A0 </span><span style=
=3D"color:#660">}</span><span style=3D"color:#000"><br><br>=C2=A0 ...<br></=
span><span style=3D"color:#660">};</span></div></code></div><br><p></p>But =
as I proceed with the refactoring, I discover that the Slave class used to =
provide a method that shares access to its master:<br><br><div style=3D"bac=
kground-color:rgb(250,250,250);border-color:rgb(187,187,187);border-style:s=
olid;border-width:1px"><code><div><span style=3D"color:#000">std</span><spa=
n style=3D"color:#660">::</span><span style=3D"color:#000">shared_ptr</span=
><span style=3D"color:#660"><</span><span style=3D"color:#606">Master</s=
pan><span style=3D"color:#660">></span><span style=3D"color:#000"> </spa=
n><span style=3D"color:#606">Slave</span><span style=3D"color:#660">::</spa=
n><span style=3D"color:#000">getMaster</span><span style=3D"color:#660">()<=
/span><span style=3D"color:#000"> </span><span style=3D"color:#008">const</=
span><span style=3D"color:#000"><br></span><span style=3D"color:#660">{</sp=
an><span style=3D"color:#000"><br>=C2=A0 </span><span style=3D"color:#008">=
return</span><span style=3D"color:#000"> m_master</span><span style=3D"colo=
r:#660">;</span><span style=3D"color:#000"><br></span><span style=3D"color:=
#660">}</span></div></code></div><br>This does not compile anymore. And I&#=
39;m happy about that: it should never have compiled to begin with. Returni=
ng non-const access to my members from a const method definitely does not m=
atch my idea of const-correctness!<br><br>Instead, I would like to only pro=
vide const access to the master object, like so:<br><br><div style=3D"backg=
round-color:rgb(250,250,250);border-color:rgb(187,187,187);border-style:sol=
id;border-width:1px"><code><div><span style=3D"color:#000">std</span><span =
style=3D"color:#660">::</span><span style=3D"color:#000">shared_ptr</span><=
span style=3D"color:#660"><</span><span style=3D"color:#008">const</span=
><span style=3D"color:#000"> </span><span style=3D"color:#606">Master</span=
><span style=3D"color:#660">></span><span style=3D"color:#000"> </span><=
span style=3D"color:#606">Slave</span><span style=3D"color:#660">::</span><=
span style=3D"color:#000">getMaster</span><span style=3D"color:#660">()</sp=
an><span style=3D"color:#000"> </span><span style=3D"color:#008">const</spa=
n><span style=3D"color:#660">;</span></div></code></div><br>If my clients a=
re well-behaved and do not mutate the master object, this will be a minimal=
ly invasive interface change. It will require only minor client rewrites, t=
he kind that could be automated by sed or an IDE. It could even require no =
client rewrite at all if I end up being lucky and have clients that were us=
ing auto and friends.<br><br>Unfortunately, the current propagate_const int=
erface does not allow me to implement this method in a clean way.<br><br>Th=
e obvious code snippet would not work due to the lack of an appropriate imp=
licit conversion:<br><br><div style=3D"background-color:rgb(250,250,250);bo=
rder-color:rgb(187,187,187);border-style:solid;border-width:1px"><code><div=
><span style=3D"color:#000">std</span><span style=3D"color:#660">::</span><=
span style=3D"color:#000">shared_ptr</span><span style=3D"color:#660"><c=
onst </span><span style=3D"color:#606">Master</span><span style=3D"color:#6=
60">></span><span style=3D"color:#000"> </span><span style=3D"color:#606=
">Slave</span><span style=3D"color:#660">::</span><span style=3D"color:#000=
">getMaster</span><span style=3D"color:#660">()</span><span style=3D"color:=
#000"> </span><span style=3D"color:#008">const</span><span style=3D"color:#=
000"><br></span><span style=3D"color:#660">{</span><span style=3D"color:#00=
0"><br>=C2=A0 </span><span style=3D"color:#008">return</span><span style=3D=
"color:#000"> m_master</span><span style=3D"color:#660">;</span><span style=
=3D"color:#000">=C2=A0 // ERROR: No implicit conversion from propagate_cons=
t<shared_ptr>!<br></span><span style=3D"color:#660">}</span></div></c=
ode></div><br>Using get() here would be the perfect way to introduce use af=
ter free bugs:<br><br><div style=3D"background-color:rgb(250,250,250);borde=
r-color:rgb(187,187,187);border-style:solid;border-width:1px"><code><div><s=
pan style=3D"color:#000">std</span><span style=3D"color:#660">::</span><spa=
n style=3D"color:#000">shared_ptr</span><span style=3D"color:#660"><cons=
t </span><span style=3D"color:#606">Master</span><span style=3D"color:#660"=
>></span><span style=3D"color:#000"> </span><span style=3D"color:#606">S=
lave</span><span style=3D"color:#660">::</span><span style=3D"color:#000">g=
etMaster</span><span style=3D"color:#660">()</span><span style=3D"color:#00=
0"> </span><span style=3D"color:#008">const</span><span style=3D"color:#000=
"><br></span><span style=3D"color:#660">{</span><span style=3D"color:#000">=
<br>=C2=A0 const Master* master =3D m_master.get();<br>=C2=A0 </span><span =
style=3D"color:#008">return</span><span style=3D"color:#000"> std::shared_p=
tr<Master>( master</span><span style=3D"color:#660"> );</span><span s=
tyle=3D"color:#000">=C2=A0 // INCORRECT: Two owners for one object!<br></sp=
an><span style=3D"color:#660">}</span></div></code></div><br>And using get_=
underlying will both break const correctness and fail to provide me with th=
e correct return type:<br><br><div style=3D"background-color:rgb(250,250,25=
0);border-color:rgb(187,187,187);border-style:solid;border-width:1px"><code=
><div><span style=3D"color:#000">std</span><span style=3D"color:#660">::</s=
pan><span style=3D"color:#000">shared_ptr</span><span style=3D"color:#660">=
<const </span><span style=3D"color:#606">Master</span><span style=3D"col=
or:#660">></span><span style=3D"color:#000"> </span><span style=3D"color=
:#606">Slave</span><span style=3D"color:#660">::</span><span style=3D"color=
:#000">getMaster</span><span style=3D"color:#660">()</span><span style=3D"c=
olor:#000"> </span><span style=3D"color:#008">const</span><span style=3D"co=
lor:#000"><br></span><span style=3D"color:#660">{</span><span style=3D"colo=
r:#000"><br>=C2=A0 auto master =3D std::get_underlying( m_master );=C2=A0 /=
/ This is an std::shared_ptr<Master><br>=C2=A0 </span><span style=3D"=
color:#008">return</span><span style=3D"color:#000"> master;=C2=A0 // ERROR=
: No implicit conversion to std::shared_ptr<const Master>!</span><spa=
n style=3D"color:#000"><br></span><span style=3D"color:#660">}</span></div>=
</code></div><br>To implement this method, in addition to the const-incorre=
ct get_underlying interface, I would also need a const cast!<br><br><div st=
yle=3D"background-color:rgb(250,250,250);border-color:rgb(187,187,187);bord=
er-style:solid;border-width:1px"><code><div><span style=3D"color:#000">std<=
/span><span style=3D"color:#660">::</span><span style=3D"color:#000">shared=
_ptr</span><span style=3D"color:#660"><const </span><span style=3D"color=
:#606">Master</span><span style=3D"color:#660">></span><span style=3D"co=
lor:#000"> </span><span style=3D"color:#606">Slave</span><span style=3D"col=
or:#660">::</span><span style=3D"color:#000">getMaster</span><span style=3D=
"color:#660">()</span><span style=3D"color:#000"> </span><span style=3D"col=
or:#008">const</span><span style=3D"color:#000"><br></span><span style=3D"c=
olor:#660">{</span><span style=3D"color:#000"><br>=C2=A0 auto master =3D st=
d::get_underlying( m_master ); <br>=C2=A0 </span><span style=3D"color:#008"=
>return</span><span style=3D"color:#000"> std::const_pointer_cast<const =
Master>( master );</span><span style=3D"color:#000"><br></span><span sty=
le=3D"color:#660">}</span></div></code></div><p><br></p><p>For sure, I woul=
d never want an abomination like this to pass code review :)</p><p><br></p>=
<p>This little thought experiment showcases two major issues with get_under=
lying:</p><ul><li>Counter to the goal of of propagate_const, it makes it tr=
ivial to violate const-correctness without the compiler noticing (or, for t=
hat matter, any developer or static analysis tool that is unfamiliar with t=
he propagate_const API).<br></li><li>By not returning the const-correct typ=
e, get_underlying can make it unnecessarily hard to implement const-correct=
interfaces.<br></li></ul><p></p><p>The authors of the original propagate_c=
onst proposal were aware that the get_underlying interface was suboptimal, =
and for this reason they opted to make it less usable by hiding it as a fre=
e function instead of making it a proper method of propagate_const. But per=
sonnally, I would go further: drop get_underlying altogether, and replace i=
t with something that is both const-correct and respectful of pointer owner=
ship issues.<br></p><p><br></p><p>My counter-proposal would be to have a pr=
opagate_const method, maybe called "share()", which enables shari=
ng access to the data pointed by propagate_const with due respect paid to b=
oth const-correctness and the underlying pointer's ownership semantics.=
In practice:</p><ul><li>If called on propagate_const<T*>, share() sh=
ould return a T*</li><li>If called on const propagate_const<T*>, shar=
e() should return a const T*</li><li>If called on propagate_const<shared=
_ptr<T>><wbr>, share() should return a shared_ptr<T></li><li=
>If called on const propagate_const<shared_ptr<T>><wbr>, share(=
) should return a shared_ptr<const T></li><li>share() should not be d=
efined on propagate_const<unique_ptr<T>> since that pointer doe=
s not support sharing ownership</li></ul></div></blockquote><div>This all s=
eems great, but...</div><div>"const propagate_const<shared_ptr<T=
>><wbr>, share() should return a shared_ptr<const T>"<br><=
/div><div>How do you envision this being specified? =C2=A0Remember that pro=
pagate_const is just a template. So either you have to come up with generic=
semantics that produce this result "by happy accident", or else =
you have to decide that propagate_const is "built-in magic" and o=
nly works for std::shared_ptr (and <i><b>not</b></i> boost::shared_ptr<T=
> and <i><b>not</b></i> dropbox::nn<T*> and so on), or else you ha=
ve to wade into the customization-point swamp. Which route would you like t=
o take?</div><div><br></div><div>=E2=80=93Arthur</div><div><br></div><div>P=
..S.: I do think that <a href=3D"http://en.cppreference.com/w/cpp/experiment=
al/propagate_const/get">propagate_const<T>::get()</a> is currently mi=
s-specified; it's proposed to return <font face=3D"courier new, monospa=
ce">t_.get()</font> when the actually useful value to return would be <font=
face=3D"courier new, monospace">t_</font>. But we already had a thread abo=
ut that. ;)</div></div>
<p></p>
-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/519ae870-041a-4197-aa9f-90ed82aa26f3%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/519ae870-041a-4197-aa9f-90ed82aa26f3=
%40isocpp.org</a>.<br />
------=_Part_714_1604873098.1487729684917--
------=_Part_713_690374026.1487729684916--
.
Author: Hadrien Grasland <hadrien.grasland@gmail.com>
Date: Tue, 21 Feb 2017 23:20:12 -0800 (PST)
Raw View
------=_Part_1709_2184887.1487748012744
Content-Type: text/plain; charset=UTF-8
I'm glad you asked this question. As someone who has always been ticked off by the gratuitous inconsistency of const_cast vs const_pointer_cast, the answer would be obvious to me:
1/ Introduce a type trait, maybe called pointer_constness_traits, which given a pointer-to-mutable can tell you what is the associated pointer-to-const type (and vice versa).
2/ Change the semantics of "const_pointer_cast" from "some weirdly restricted function template that only works on shared_ptr" to "a truly general mechanism for turning pointer-to-const to pointer-to-mutable" (and vice versa).
3/ Add an overload for raw pointers, and leave it up to smart pointer library implementors to add their own specializations of the type trait and const_pointer_cast.
See boost::pointer_cast for prior art.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/76b3d7b0-9619-44b5-bc68-a37522e40686%40isocpp.org.
------=_Part_1709_2184887.1487748012744--
.
Author: Jonathan Coe <jonathanbcoe@gmail.com>
Date: Wed, 22 Feb 2017 08:55:27 +0000
Raw View
--94eb2c190d14e1a6f905491aaaff
Content-Type: text/plain; charset=UTF-8
On 21 February 2017 at 14:54, Hadrien Grasland <hadrien.grasland@gmail.com>
wrote:
> Hi everyone,
>
> I am not sure what this group's policy is regarding resurrection of old
> threads, so I have decided to create a new one. But I would like to second
> two comments that were previously made on the propagate_const proposal of
> N4617:
>
> 1/ That it should be possible to copy a propagate_const<T> wrapper (
> https://groups.google.com/a/isocpp.org/forum/#!searchin/
> std-proposals/propagate_const/std-proposals/1uDKcA9bssU/AZ-aQNAZAAAJ ).
> 2/ That propagate_const<T> should be convertible to a const-correct form
> of the underlying pointer type ( https://groups.google.com/a/
> isocpp.org/forum/#!searchin/std-proposals/propagate_const/
> std-proposals/7rSMtvQVASk/niLSKtkcBwAJ ).
>
> Let me motivate, through some concrete use cases, why I think that both of
> these changes are necessary.
>
>
> Need for shallow copy operations
>
> There are many reasons to use pointers in C++. A very common use case,
> however, is dynamic polymorphism:
>
> - Different implementations of a virtual interface may have different
> sizes
> - C++, in its commendable quest for zero-cost abstraction, does not
> provide native support for dynamically sized types
> - When combined, these two language design choices lead to the
> conclusion that if the set of implementations of an interface is not known
> as compile time, polymorphism intrinsically requires pointer indirection in
> C++
>
> For this use case, pointer constness semantics are inadequate. If you use
> a pointer-to-const, you cannot modify the target object, which is often
> fine but tends to be excessively limitating for some use cases,
> particularly when implementing constructors and factory functions. If, on
> the other hand, you use a pointer-to-mutable, you open a const-correctness
> hole by allowing yourself to mutate a const object without any compiler
> warning. Quite frankly, none of these options are very appealing.
>
> // Pointer-to-const restricts some valid use cases of polymorphism
> class FirstTry
> {
> private:
> std::unique_ptr<const Interface> m_ptr;
>
> public:
> FirstTry()
> : m_ptr{ std::make_unique<Implementation>( ... ) }
> {
> m_ptr->setParent( *this ); // ERROR: Can't mutate through a const
> pointer!
> }
> };
>
>
> // Pointer-to-mutable breaks const correctness of polymorphism
> class SecondTry
> {
> private:
> std::unique_ptr<Interface> m_ptr;
>
> public:
> SecondTry()
> : m_ptr{ std::make_unique<Implementation>( ... ) }
> {
> m_ptr->setParent( *this ); // This is now okay...
> }
>
> int someRandomAccessor() const {
> m_ptr->setSomething(); // ...but unfortunately, this is okay too :-(
> return 42;
> }
> };
>
> Now, if we are on the same wavelength, and I will assume in the following
> that this is the case, this is precisely the kind of issue that
> propagate_const is designed to help with.
>
> Let us now turn our attention to copy operations. Ideally, polymorphic
> objects should be deep-copyable, just like any regular value type.
> Unfortunately, in another application of the zero cost abstraction
> principle, C++ provides no easy way to do this. Anyone who wants to combine
> value semantics and dynamic polymorphism needs to provide explicit support
> for deep copies across the entire class hierarchy, which is cumbersome to
> begin with and can get problematic when interacting with third-party
> libraries.
>
Arthur has addressed your concerns with the same points I would have raised
(thanks Arthur). On the related topic of deep-copies and const-propagation
we're working on polymorphic_value which aims to tackle copies through
class heirarchies. I'd be interested if you have a use case where one of
propagate_const and polymorphic_value does not solve your problem.
https://github.com/jbcoe/polymorphic_value/blob/master/talks/2017_1_25_cxx_london.md
https://github.com/jbcoe/polymorphic_value/blob/master/draft.md
> class Interface
> {
> public:
> // This kind of boilerplate must be added to every class supporting
> deep copy...
> virtual std::unique_ptr<Interface> clone() const = 0;
> };
>
> class Implementation : public Interface
> {
> public:
> // ...and replicated again and again in every single non-abstract
> child. No fun.
> std::unique_ptr<Interface> clone() const final override
> {
> return std::make_unique<Implementation>( *this );
> }
> };
>
> But thankfully, sometimes, we can do without a true deep copy, and satisfy
> ourselves with a shallow copy. And in fact, for some use cases, a shallow
> copy will even be exactly what we want:
>
> - When building some variety of search acceleration structure
> (hashmap, neighbour locator, axis-aligned-bounding box...)
> - When many "slave objects" share a reference to some common,
> potentially large "master object"
>
> Unfortunately, even though all standard C++ pointer types make it trivial
> to shallow-copy a polymorphic object, propagate_const gets in our way here
> by being move only. I see this design choice as a shortcoming, because it
> reduces the usefulness of propagate_const in many dynamic polymorphism use
> cases where the underlying pointer type would have done just fine. For
> example, without shallow copy operations...
>
> - You cannot let the compiler implement a shallow copy constructor for
> you using "= default"
> - You cannot use std::copy_if to find objects matching some predicate
> in an internal dataset
> - You cannot easily build object search acceleration structures, such
> as hashmaps or AABBs
> - You cannot easily share a reference to an object wrapped by
> propagate_const with other objects
>
> I am aware that shallow copies are possible using the get() and
> get_underlying() operations. I do not see this as a satisfactory answer,
> because it breaks every STL-ish algorithm that expects a copy constructor.
> Of course, I could build a propagate_const wrapper that has a copy
> constructor myself, and this is what I would end up doing if
> propagate_const were accepted in the STL in its current form. But I think
> that the use cases that I mentioned above are valid enough to warrant
> changing the design of propagate_const instead.
>
I am aware that in order to be const-correct, a shallow copy constructor
> for propagate_const would need to operate from a non-const reference. I am
> fine with this tradeoff. The main reason we usually allow ourselves to make
> mutable copies from const objects is because we assume the copy to be
> independent from the original object. This assumption is broken when making
> shallow copies through pointer or reference types. Sure, many APIs will be
> broken initially, but that is unavoidable when fixing old programming
> language flaws. Bugs will be reported, and interfaces will be fixed, the
> way it's always been done.
>
>
> For prior art, if you look at the STL's documentation, you will find that
> customizable algorithms that copy const data, such as std::copy_if,
> explicitly do NOT require the user-provided functions to consume the data
> by const-reference. So the idea of copying from non-const is not new. The
> C++ community only needs to re-discover it.
>
>
>
We considered making copies from non-const propagate_const legal but were
strongly dissuaded by 3 major standard library implementers.
> I am also aware of the objection, made in a thread linked above, that "We
> were not confident that a non-const copy constructor would protect the user
> from accidental loss of const and accidental mutable shared state". As far
> as I'm concerned, this hand-waving statement was not properly justified.
> From my perspective, the proposed alternative of using get_underlying seems
> to be much bigger breach of const-correctness than a copy constructor that
> operates from mutable, because from a semantic point of view,
> get_underlying is essentially a silent const_cast that standard code
> analysis tools won't catch.
>
>
>
We added `get_underlying` as an nicer alternative to `reinterpret_cast`,
which would give access to the underlying pointer.
Our design sought to avoid silent surprise. Granted a user can do the wrong
thing with get_underlying but the code is written by the user, not by the
compiler. Forcing the user to be explicit offers some protection.
>
> Const-correct convertibility to the underlying pointer type
>
> Sometimes, one has to extract a pointer back from the propagate_const
> wrapper. I would see two main use cases for this:
>
> - To feed a legacy API that is not (yet?) compatible with
> propagate_const.
> - To turn a propagate_const<T> into a pointer-to-const, when the
> recipient should not be able to modify to the target object
>
> The current propagate_const interface provides three ways to do this:
>
> - Call get() and get a raw pointer
> - Rely on an implicit cast to raw pointer, if available
> - Use get_underlying to access the internals of propagate_const.
>
> As I'm going to elaborate, the first two operations are not always the
> right tool for the job at hand, because they fail to convey important
> ownership information from the underlying pointer type. Whereas
> get_underlying, as currently implemented, is a flawed interface that goes
> directly against the design goals of propagate_const and should be
> eradicated before standardization.
>
>
> As a use case, suppose that as discussed previously, I am working a "slave
> object", which holds a shared_ptr to an associated "master object" that it
> shares with other slaves. To improve the const-correctness of this design,
> I have decided to refactor the shared_ptr into a
> propagate_const<shared_ptr>. So far, so good.
>
>
> class Master
> {
> ...
> };
>
> class Slave
> {
> private:
> // This was refactored from "std::shared_ptr<Master> m_master"
> std::propagate_const<std::shared_ptr<Master>> m_master;
>
> public:
> Slave( std::shared_ptr<Master> master )
> : m_master(master)
> {
> m_master->addSlave( *this );
> }
>
> ...
> };
>
But as I proceed with the refactoring, I discover that the Slave class used
> to provide a method that shares access to its master:
>
> std::shared_ptr<Master> Slave::getMaster() const
> {
> return m_master;
> }
>
> This does not compile anymore. And I'm happy about that: it should never
> have compiled to begin with. Returning non-const access to my members from
> a const method definitely does not match my idea of const-correctness!
>
> Instead, I would like to only provide const access to the master object,
> like so:
>
> std::shared_ptr<const Master> Slave::getMaster() const;
>
> If my clients are well-behaved and do not mutate the master object, this
> will be a minimally invasive interface change. It will require only minor
> client rewrites, the kind that could be automated by sed or an IDE. It
> could even require no client rewrite at all if I end up being lucky and
> have clients that were using auto and friends.
>
> Unfortunately, the current propagate_const interface does not allow me to
> implement this method in a clean way.
>
>
Sadly that's intended. We can't guarantee that "clients are well-behaved
and do not mutate the master object".
> The obvious code snippet would not work due to the lack of an appropriate
> implicit conversion:
>
> std::shared_ptr<const Master> Slave::getMaster() const
> {
> return m_master; // ERROR: No implicit conversion from
> propagate_const<shared_ptr>!
> }
>
> Using get() here would be the perfect way to introduce use after free bugs:
>
> std::shared_ptr<const Master> Slave::getMaster() const
> {
> const Master* master = m_master.get();
> return std::shared_ptr<Master>( master ); // INCORRECT: Two owners for
> one object!
> }
>
> And using get_underlying will both break const correctness and fail to
> provide me with the correct return type:
>
> std::shared_ptr<const Master> Slave::getMaster() const
> {
> auto master = std::get_underlying( m_master ); // This is an
> std::shared_ptr<Master>
> return master; // ERROR: No implicit conversion to
> std::shared_ptr<const Master>!
> }
>
> To implement this method, in addition to the const-incorrect
> get_underlying interface, I would also need a const cast!
>
> std::shared_ptr<const Master> Slave::getMaster() const
> {
> auto master = std::get_underlying( m_master );
> return std::const_pointer_cast<const Master>( master );
> }
>
>
> For sure, I would never want an abomination like this to pass code review
> :)
>
>
> Agreed.
> This little thought experiment showcases two major issues with
> get_underlying:
>
> - Counter to the goal of of propagate_const, it makes it trivial to
> violate const-correctness without the compiler noticing (or, for that
> matter, any developer or static analysis tool that is unfamiliar with the
> propagate_const API).
> - By not returning the const-correct type, get_underlying can make it
> unnecessarily hard to implement const-correct interfaces.
>
> The authors of the original propagate_const proposal were aware that the
> get_underlying interface was suboptimal, and for this reason they opted to
> make it less usable by hiding it as a free function instead of making it a
> proper method of propagate_const. But personnally, I would go further: drop
> get_underlying altogether, and replace it with something that is both
> const-correct and respectful of pointer ownership issues.
>
>
> My counter-proposal would be to have a propagate_const method, maybe
> called "share()", which enables sharing access to the data pointed by
> propagate_const with due respect paid to both const-correctness and the
> underlying pointer's ownership semantics. In practice:
>
> - If called on propagate_const<T*>, share() should return a T*
> - If called on const propagate_const<T*>, share() should return a
> const T*
> - If called on propagate_const<shared_ptr<T>>, share() should return a
> shared_ptr<T>
> - If called on const propagate_const<shared_ptr<T>>, share() should
> return a shared_ptr<const T>
> - share() should not be defined on propagate_const<unique_ptr<T>>
> since that pointer does not support sharing ownership
>
> In addition, as a convenience, whenever a share() method exists, an
> implicit cast could be provided to the underlying pointer type as a way to
> make such ownership sharing more convenient and to enable things like
> comparison of containers of shared_ptr<T> with containers of
> propagate_const<shared_ptr<T>> through standard (STL-ish) algorithms.
>
>
> If you really, positively want something like get_underlying to exist, it
> should only be applicable to non-const object. This alone would make it
> const-correct, and thus remove the need to implement it as a free function
> and be careful when using it.
>
>
> With this simple change, shameless people who want to get their hands
> dirty and extract a mutable pointer from a const wrapper will then need to
> do some extra work, as should be expected of them in my opinion:
>
>
> using ConstPropagator = std::propagate_const<std::shared_ptr<T>>;
>
> const ConstPropagator& immutableRef = ... ;
> std::shared_ptr<T> mutableRef = const_cast<ConstPropagator*>( &immutableRef
> )->get_underlying();
>
> --
> You received this message because you are subscribed to the Google Groups
> "ISO C++ Standard - Future Proposals" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to std-proposals+unsubscribe@isocpp.org.
> To post to this group, send email to std-proposals@isocpp.org.
> To view this discussion on the web visit https://groups.google.com/a/
> isocpp.org/d/msgid/std-proposals/9ad3f22a-a619-4488-
> bf1e-303641c968b9%40isocpp.org
> <https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/9ad3f22a-a619-4488-bf1e-303641c968b9%40isocpp.org?utm_medium=email&utm_source=footer>
> .
>
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAAbBDD8Xw09taeNMuGtpaozWsDCo68y8TOjAXk%2BhfNanJi_XZg%40mail.gmail.com.
--94eb2c190d14e1a6f905491aaaff
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr"><br><div class=3D"gmail_extra"><br><div class=3D"gmail_quo=
te">On 21 February 2017 at 14:54, Hadrien Grasland <span dir=3D"ltr"><<a=
href=3D"mailto:hadrien.grasland@gmail.com" target=3D"_blank">hadrien.grasl=
and@gmail.com</a>></span> wrote:<br><blockquote class=3D"gmail_quote" st=
yle=3D"margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb=
(204,204,204);border-left-style:solid;padding-left:1ex"><div dir=3D"ltr">Hi=
everyone,<br><br>I am not sure what this group's policy is regarding r=
esurrection of old threads, so I have decided to create a new one. But I wo=
uld like to second two comments that were previously made on the propagate_=
const proposal of N4617:<br><br>1/ That it should be possible to copy a pro=
pagate_const<T> wrapper ( <a href=3D"https://groups.google.com/a/isoc=
pp.org/forum/#!searchin/std-proposals/propagate_const/std-proposals/1uDKcA9=
bssU/AZ-aQNAZAAAJ" target=3D"_blank">https://groups.google.com/a/<wbr>isocp=
p.org/forum/#!searchin/<wbr>std-proposals/propagate_const/<wbr>std-proposal=
s/1uDKcA9bssU/AZ-<wbr>aQNAZAAAJ</a> ).<br>2/ That propagate_const<T> =
should be convertible to a const-correct form of the underlying pointer typ=
e ( <a href=3D"https://groups.google.com/a/isocpp.org/forum/#!searchin/std-=
proposals/propagate_const/std-proposals/7rSMtvQVASk/niLSKtkcBwAJ" target=3D=
"_blank">https://groups.google.com/a/<wbr>isocpp.org/forum/#!searchin/<wbr>=
std-proposals/propagate_const/<wbr>std-proposals/7rSMtvQVASk/<wbr>niLSKtkcB=
wAJ</a> ).<br><br>Let me motivate, through some concrete use cases, why I t=
hink that both of these changes are necessary.<br><br><br><font size=3D"4">=
Need for shallow copy operations</font><br><br>There are many reasons to us=
e pointers in C++. A very common use case, however, is dynamic polymorphism=
:<br><ul><li>Different implementations of a virtual interface may have diff=
erent sizes</li><li>C++, in its commendable quest for zero-cost abstraction=
, does not provide native support for dynamically sized types</li><li>When =
combined, these two language design choices lead to the conclusion that if =
the set of implementations of an interface is not known as compile time, po=
lymorphism intrinsically requires pointer indirection in C++</li></ul>For t=
his use case, pointer constness semantics are inadequate. If you use a poin=
ter-to-const, you cannot modify the target object, which is often fine but =
tends to be excessively limitating for some use cases, particularly when im=
plementing constructors and factory functions. If, on the other hand, you u=
se a pointer-to-mutable, you open a const-correctness hole by allowing your=
self to mutate a const object without any compiler warning. Quite frankly, =
none of these options are very appealing.<br><br><div style=3D"background-c=
olor:rgb(250,250,250);border:1px solid rgb(187,187,187)" class=3D"gmail-m_-=
1550785340728421304prettyprint"><code class=3D"gmail-m_-1550785340728421304=
prettyprint"><div class=3D"gmail-m_-1550785340728421304subprettyprint"><spa=
n style=3D"color:rgb(136,0,0)" class=3D"gmail-m_-1550785340728421304styled-=
by-prettify">// Pointer-to-const restricts some valid use cases of polymorp=
hism</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728=
421304styled-by-prettify"><br></span><span style=3D"color:rgb(0,0,136)" cla=
ss=3D"gmail-m_-1550785340728421304styled-by-prettify">class</span><span sty=
le=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-pret=
tify"> </span><span style=3D"color:rgb(102,0,102)" class=3D"gmail-m_-155078=
5340728421304styled-by-prettify">FirstTry</span><span style=3D"color:rgb(0,=
0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"><br></span><=
span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304st=
yled-by-prettify">{</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m=
_-1550785340728421304styled-by-prettify"></span><br><code class=3D"gmail-m_=
-1550785340728421304prettyprint"><span style=3D"color:rgb(0,0,136)" class=
=3D"gmail-m_-1550785340728421304styled-by-prettify">private</span><span sty=
le=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-by-=
prettify">:</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-155078=
5340728421304styled-by-prettify"><br>=C2=A0 std</span><span style=3D"color:=
rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">::=
</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-15507853407284213=
04styled-by-prettify">unique_ptr</span><span style=3D"color:rgb(102,102,0)"=
class=3D"gmail-m_-1550785340728421304styled-by-prettify"><</span><span =
style=3D"color:rgb(0,0,136)" class=3D"gmail-m_-1550785340728421304styled-by=
-prettify">const</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1=
550785340728421304styled-by-prettify"> </span><span style=3D"color:rgb(102,=
0,102)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">Interface<=
/span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-15507853407284=
21304styled-by-prettify">></span><span style=3D"color:rgb(0,0,0)" class=
=3D"gmail-m_-1550785340728421304styled-by-prettify"> m_ptr</span><span styl=
e=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-by-p=
rettify">;</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785=
340728421304styled-by-prettify"><br><br></span></code><span style=3D"color:=
rgb(0,0,136)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">publ=
ic</span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-15507853407=
28421304styled-by-prettify">:</span><span style=3D"color:rgb(0,0,0)" class=
=3D"gmail-m_-1550785340728421304styled-by-prettify"><br>=C2=A0 </span><span=
style=3D"color:rgb(102,0,102)" class=3D"gmail-m_-1550785340728421304styled=
-by-prettify">FirstTry</span><span style=3D"color:rgb(102,102,0)" class=3D"=
gmail-m_-1550785340728421304styled-by-prettify">()</span><span style=3D"col=
or:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"><br=
>=C2=A0 =C2=A0 </span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m=
_-1550785340728421304styled-by-prettify">:</span><span style=3D"color:rgb(0=
,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"> m_ptr</spa=
n><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-155078534072842130=
4styled-by-prettify">{</span><span style=3D"color:rgb(0,0,0)" class=3D"gmai=
l-m_-1550785340728421304styled-by-prettify"> std</span><span style=3D"color=
:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">:=
:</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421=
304styled-by-prettify">make_unique</span><span style=3D"color:rgb(102,102,0=
)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"><</span><spa=
n style=3D"color:rgb(102,0,102)" class=3D"gmail-m_-1550785340728421304style=
d-by-prettify">Implementatio<wbr>n</span><span style=3D"color:rgb(102,102,0=
)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">>(</span><sp=
an style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-b=
y-prettify"> </span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-=
1550785340728421304styled-by-prettify">...</span><span style=3D"color:rgb(0=
,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"> </span><sp=
an style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styl=
ed-by-prettify">)</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-=
1550785340728421304styled-by-prettify"> </span><span style=3D"color:rgb(102=
,102,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">}</span><=
span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled=
-by-prettify"><br>=C2=A0 </span><span style=3D"color:rgb(102,102,0)" class=
=3D"gmail-m_-1550785340728421304styled-by-prettify">{</span><span style=3D"=
color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">=
<br>=C2=A0 =C2=A0 m_ptr</span><span style=3D"color:rgb(102,102,0)" class=3D=
"gmail-m_-1550785340728421304styled-by-prettify">-></span><span style=3D=
"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"=
>setParent</span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-155=
0785340728421304styled-by-prettify">(</span><span style=3D"color:rgb(0,0,0)=
" class=3D"gmail-m_-1550785340728421304styled-by-prettify"> </span><span st=
yle=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-by=
-prettify">*</span><span style=3D"color:rgb(0,0,136)" class=3D"gmail-m_-155=
0785340728421304styled-by-prettify">this</span><span style=3D"color:rgb(0,0=
,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"> </span><span=
style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled=
-by-prettify">);</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1=
550785340728421304styled-by-prettify"> =C2=A0</span><span style=3D"color:rg=
b(136,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">// ERR=
OR:</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-15507853407284=
21304styled-by-prettify"> Can't mutate through a const pointer!<br>=C2=
=A0 </span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-155078534=
0728421304styled-by-prettify">}</span><span style=3D"color:rgb(0,0,0)" clas=
s=3D"gmail-m_-1550785340728421304styled-by-prettify"><br></span><span style=
=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-by-pr=
ettify">};</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785=
340728421304styled-by-prettify"><br><br><br></span><span style=3D"color:rgb=
(136,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">// Poin=
ter-to-mutable breaks const correctness of polymorphism</span><span style=
=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-pretti=
fy"><br></span><span style=3D"color:rgb(0,0,136)" class=3D"gmail-m_-1550785=
340728421304styled-by-prettify">class</span><span style=3D"color:rgb(0,0,0)=
" class=3D"gmail-m_-1550785340728421304styled-by-prettify"> </span><span st=
yle=3D"color:rgb(102,0,102)" class=3D"gmail-m_-1550785340728421304styled-by=
-prettify">SecondTry</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-=
m_-1550785340728421304styled-by-prettify"><br></span><span style=3D"color:r=
gb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">{</=
span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304=
styled-by-prettify"><br>private:<br>=C2=A0 std::unique_ptr<Interface>=
m_ptr;<br><br></span><span style=3D"color:rgb(0,0,136)" class=3D"gmail-m_-=
1550785340728421304styled-by-prettify">public</span><span style=3D"color:rg=
b(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">:</s=
pan><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304s=
tyled-by-prettify"><br>=C2=A0 </span><span style=3D"color:rgb(102,0,102)" c=
lass=3D"gmail-m_-1550785340728421304styled-by-prettify">SecondTry</span><sp=
an style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styl=
ed-by-prettify">()</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_=
-1550785340728421304styled-by-prettify"><br>=C2=A0 =C2=A0 </span><span styl=
e=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-by-p=
rettify">:</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785=
340728421304styled-by-prettify"> m_ptr</span><span style=3D"color:rgb(102,1=
02,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">{</span><sp=
an style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-b=
y-prettify"> std</span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-=
m_-1550785340728421304styled-by-prettify">::</span><span style=3D"color:rgb=
(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">make_uniq=
ue</span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-15507853407=
28421304styled-by-prettify"><</span><span style=3D"color:rgb(102,0,102)"=
class=3D"gmail-m_-1550785340728421304styled-by-prettify">Implementatio<wbr=
>n</span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-15507853407=
28421304styled-by-prettify">>(</span><span style=3D"color:rgb(0,0,0)" cl=
ass=3D"gmail-m_-1550785340728421304styled-by-prettify"> </span><span style=
=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-by-pr=
ettify">...</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-155078=
5340728421304styled-by-prettify"> </span><span style=3D"color:rgb(102,102,0=
)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">)</span><span s=
tyle=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-pr=
ettify"> </span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550=
785340728421304styled-by-prettify">}</span><span style=3D"color:rgb(0,0,0)"=
class=3D"gmail-m_-1550785340728421304styled-by-prettify"><br>=C2=A0 </span=
><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304=
styled-by-prettify">{</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail=
-m_-1550785340728421304styled-by-prettify"><br>=C2=A0 =C2=A0 m_ptr</span><s=
pan style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304sty=
led-by-prettify">-></span><span style=3D"color:rgb(0,0,0)" class=3D"gmai=
l-m_-1550785340728421304styled-by-prettify">setParent</span><span style=3D"=
color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-by-pretti=
fy">(</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-155078534072=
8421304styled-by-prettify"> </span><span style=3D"color:rgb(102,102,0)" cla=
ss=3D"gmail-m_-1550785340728421304styled-by-prettify">*</span><span style=
=3D"color:rgb(0,0,136)" class=3D"gmail-m_-1550785340728421304styled-by-pret=
tify">this</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785=
340728421304styled-by-prettify"> </span><span style=3D"color:rgb(102,102,0)=
" class=3D"gmail-m_-1550785340728421304styled-by-prettify">);</span><span s=
tyle=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-pr=
ettify"> =C2=A0</span><span style=3D"color:rgb(136,0,0)" class=3D"gmail-m_-=
1550785340728421304styled-by-prettify">// This is now okay...</span><span s=
tyle=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-pr=
ettify"><br>=C2=A0 </span><span style=3D"color:rgb(102,102,0)" class=3D"gma=
il-m_-1550785340728421304styled-by-prettify">}</span><span style=3D"color:r=
gb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"><br><br=
>=C2=A0 </span><span style=3D"color:rgb(0,0,136)" class=3D"gmail-m_-1550785=
340728421304styled-by-prettify">int</span><span style=3D"color:rgb(0,0,0)" =
class=3D"gmail-m_-1550785340728421304styled-by-prettify"> someRandomAccesso=
r</span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-155078534072=
8421304styled-by-prettify">()</span><span style=3D"color:rgb(0,0,0)" class=
=3D"gmail-m_-1550785340728421304styled-by-prettify"> </span><span style=3D"=
color:rgb(0,0,136)" class=3D"gmail-m_-1550785340728421304styled-by-prettify=
">const</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340=
728421304styled-by-prettify"> </span><span style=3D"color:rgb(102,102,0)" c=
lass=3D"gmail-m_-1550785340728421304styled-by-prettify">{</span><span style=
=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-pretti=
fy"><br>=C2=A0 =C2=A0 m_ptr</span><span style=3D"color:rgb(102,102,0)" clas=
s=3D"gmail-m_-1550785340728421304styled-by-prettify">-></span><span styl=
e=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prett=
ify">setSomething</span><span style=3D"color:rgb(102,102,0)" class=3D"gmail=
-m_-1550785340728421304styled-by-prettify">();</span><span style=3D"color:r=
gb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"> =C2=A0=
</span><span style=3D"color:rgb(136,0,0)" class=3D"gmail-m_-155078534072842=
1304styled-by-prettify">// ...but unfortunately, this is okay too :-(</span=
><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styl=
ed-by-prettify"><br>=C2=A0 =C2=A0 </span><span style=3D"color:rgb(0,0,136)"=
class=3D"gmail-m_-1550785340728421304styled-by-prettify">return</span><spa=
n style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by=
-prettify"> </span><span style=3D"color:rgb(0,102,102)" class=3D"gmail-m_-1=
550785340728421304styled-by-prettify">42</span><span style=3D"color:rgb(102=
,102,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">;</span><=
span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled=
-by-prettify"><br>=C2=A0 </span><span style=3D"color:rgb(102,102,0)" class=
=3D"gmail-m_-1550785340728421304styled-by-prettify">}</span><span style=3D"=
color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">=
<br>};</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-15507853407=
28421304styled-by-prettify"><br></span></div></code></div><br>Now, if we ar=
e on the same wavelength, and I will assume in the following that this is t=
he case, this is precisely the kind of issue that propagate_const is design=
ed to help with.<br><br>Let us now turn our attention to copy operations. I=
deally, polymorphic objects should be deep-copyable, just like any regular =
value type. Unfortunately, in another application of the zero cost abstract=
ion principle, C++ provides no easy way to do this. Anyone who wants to com=
bine value semantics and dynamic polymorphism needs to provide explicit sup=
port for deep copies across the entire class hierarchy, which is cumbersome=
to begin with and can get problematic when interacting with third-party li=
braries.<br></div></blockquote><div><br></div><div>Arthur has addressed you=
r concerns with the same points I would have raised (thanks Arthur). On the=
related topic of deep-copies and const-propagation we're working on po=
lymorphic_value which aims to tackle copies through class heirarchies. I=
9;d be interested if you have a use case where one of propagate_const and p=
olymorphic_value does not solve your problem.=C2=A0</div><div><br></div><di=
v><a href=3D"https://github.com/jbcoe/polymorphic_value/blob/master/talks/2=
017_1_25_cxx_london.md">https://github.com/jbcoe/polymorphic_value/blob/mas=
ter/talks/2017_1_25_cxx_london.md</a><br></div><div><a href=3D"https://gith=
ub.com/jbcoe/polymorphic_value/blob/master/draft.md">https://github.com/jbc=
oe/polymorphic_value/blob/master/draft.md</a><br></div><div><br></div><div>=
<br></div><blockquote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8=
ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-sty=
le:solid;padding-left:1ex"><div dir=3D"ltr"><br><div style=3D"background-co=
lor:rgb(250,250,250);border:1px solid rgb(187,187,187)" class=3D"gmail-m_-1=
550785340728421304prettyprint"><code class=3D"gmail-m_-1550785340728421304p=
rettyprint"><div class=3D"gmail-m_-1550785340728421304subprettyprint"><span=
style=3D"color:rgb(0,0,136)" class=3D"gmail-m_-1550785340728421304styled-b=
y-prettify">class</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-=
1550785340728421304styled-by-prettify"> </span><span style=3D"color:rgb(102=
,0,102)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">Interface=
</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-15507853407284213=
04styled-by-prettify"><br></span><span style=3D"color:rgb(102,102,0)" class=
=3D"gmail-m_-1550785340728421304styled-by-prettify">{</span><span style=3D"=
color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">=
<br></span><span style=3D"color:rgb(0,0,136)" class=3D"gmail-m_-15507853407=
28421304styled-by-prettify">public</span><span style=3D"color:rgb(102,102,0=
)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">:</span><span s=
tyle=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-pr=
ettify"><br>=C2=A0 </span><span style=3D"color:rgb(136,0,0)" class=3D"gmail=
-m_-1550785340728421304styled-by-prettify">// This kind of boilerplate must=
be added to every class supporting deep copy...</span><span style=3D"color=
:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"><br>=
=C2=A0 </span><span style=3D"color:rgb(0,0,136)" class=3D"gmail-m_-15507853=
40728421304styled-by-prettify">virtual</span><span style=3D"color:rgb(0,0,0=
)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"> std</span><spa=
n style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304style=
d-by-prettify">::</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-=
1550785340728421304styled-by-prettify">unique_ptr</span><span style=3D"colo=
r:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">=
<</span><span style=3D"color:rgb(102,0,102)" class=3D"gmail-m_-155078534=
0728421304styled-by-prettify">Interface</span><span style=3D"color:rgb(102,=
102,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">></span=
><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styl=
ed-by-prettify"> clone</span><span style=3D"color:rgb(102,102,0)" class=3D"=
gmail-m_-1550785340728421304styled-by-prettify">()</span><span style=3D"col=
or:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"> </=
span><span style=3D"color:rgb(0,0,136)" class=3D"gmail-m_-15507853407284213=
04styled-by-prettify">const</span><span style=3D"color:rgb(0,0,0)" class=3D=
"gmail-m_-1550785340728421304styled-by-prettify"> </span><span style=3D"col=
or:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"=
>=3D</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728=
421304styled-by-prettify"> </span><span style=3D"color:rgb(0,102,102)" clas=
s=3D"gmail-m_-1550785340728421304styled-by-prettify">0</span><span style=3D=
"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-by-prett=
ify">;</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-15507853407=
28421304styled-by-prettify"><br></span><span style=3D"color:rgb(102,102,0)"=
class=3D"gmail-m_-1550785340728421304styled-by-prettify">};</span><span st=
yle=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-pre=
ttify"><br><br></span><span style=3D"color:rgb(0,0,136)" class=3D"gmail-m_-=
1550785340728421304styled-by-prettify">class</span><span style=3D"color:rgb=
(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"> </span><=
span style=3D"color:rgb(102,0,102)" class=3D"gmail-m_-1550785340728421304st=
yled-by-prettify">Implementation</span><span style=3D"color:rgb(0,0,0)" cla=
ss=3D"gmail-m_-1550785340728421304styled-by-prettify"> </span><span style=
=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-by-pr=
ettify">:</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-15507853=
40728421304styled-by-prettify"> </span><span style=3D"color:rgb(0,0,136)" c=
lass=3D"gmail-m_-1550785340728421304styled-by-prettify">public</span><span =
style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-p=
rettify"> </span><span style=3D"color:rgb(102,0,102)" class=3D"gmail-m_-155=
0785340728421304styled-by-prettify">Interface</span><span style=3D"color:rg=
b(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"><br></sp=
an><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-15507853407284213=
04styled-by-prettify">{</span><span style=3D"color:rgb(0,0,0)" class=3D"gma=
il-m_-1550785340728421304styled-by-prettify"><br></span><span style=3D"colo=
r:rgb(0,0,136)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">pu=
blic</span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-155078534=
0728421304styled-by-prettify">:</span><span style=3D"color:rgb(0,0,0)" clas=
s=3D"gmail-m_-1550785340728421304styled-by-prettify"><br>=C2=A0 </span><spa=
n style=3D"color:rgb(136,0,0)" class=3D"gmail-m_-1550785340728421304styled-=
by-prettify">// ...and replicated again and again in every single non-abstr=
act child. No fun.</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_=
-1550785340728421304styled-by-prettify"><br>=C2=A0 std</span><span style=3D=
"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-by-prett=
ify">::</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340=
728421304styled-by-prettify">unique_ptr</span><span style=3D"color:rgb(102,=
102,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"><</span=
><span style=3D"color:rgb(102,0,102)" class=3D"gmail-m_-1550785340728421304=
styled-by-prettify">Interface</span><span style=3D"color:rgb(102,102,0)" cl=
ass=3D"gmail-m_-1550785340728421304styled-by-prettify">></span><span sty=
le=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-pret=
tify"> clone</span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1=
550785340728421304styled-by-prettify">()</span><span style=3D"color:rgb(0,0=
,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"> </span><span=
style=3D"color:rgb(0,0,136)" class=3D"gmail-m_-1550785340728421304styled-b=
y-prettify">const</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-=
1550785340728421304styled-by-prettify"> </span><span style=3D"color:rgb(0,0=
,136)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">final</span=
><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styl=
ed-by-prettify"> </span><span style=3D"color:rgb(0,0,136)" class=3D"gmail-m=
_-1550785340728421304styled-by-prettify">override</span><span style=3D"colo=
r:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"><br>=
=C2=A0 </span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-155078=
5340728421304styled-by-prettify">{</span><span style=3D"color:rgb(0,0,0)" c=
lass=3D"gmail-m_-1550785340728421304styled-by-prettify"><br>=C2=A0 =C2=A0 <=
/span><span style=3D"color:rgb(0,0,136)" class=3D"gmail-m_-1550785340728421=
304styled-by-prettify">return</span><span style=3D"color:rgb(0,0,0)" class=
=3D"gmail-m_-1550785340728421304styled-by-prettify"> std</span><span style=
=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-by-pr=
ettify">::</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785=
340728421304styled-by-prettify">make_unique</span><span style=3D"color:rgb(=
102,102,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"><</=
span><span style=3D"color:rgb(102,0,102)" class=3D"gmail-m_-155078534072842=
1304styled-by-prettify">Implementatio<wbr>n</span><span style=3D"color:rgb(=
102,102,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">>(<=
/span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-155078534072842130=
4styled-by-prettify"> </span><span style=3D"color:rgb(102,102,0)" class=3D"=
gmail-m_-1550785340728421304styled-by-prettify">*</span><span style=3D"colo=
r:rgb(0,0,136)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">th=
is</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-155078534072842=
1304styled-by-prettify"> </span><span style=3D"color:rgb(102,102,0)" class=
=3D"gmail-m_-1550785340728421304styled-by-prettify">);</span><span style=3D=
"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"=
><br>=C2=A0 </span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1=
550785340728421304styled-by-prettify">}</span><span style=3D"color:rgb(0,0,=
0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"><br></span><sp=
an style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styl=
ed-by-prettify">};</span></div></code></div><br>But thankfully, sometimes, =
we can do without a true deep copy, and satisfy ourselves with a shallow co=
py. And in fact, for some use cases, a shallow copy will even be exactly wh=
at we want:<br><ul><li>When building some variety of search acceleration st=
ructure (hashmap, neighbour locator, axis-aligned-bounding box...)</li><li>=
When many "slave objects" share a reference to some common, poten=
tially large "master object"</li></ul>Unfortunately, even though =
all standard C++ pointer types make it trivial to shallow-copy a polymorphi=
c object, propagate_const gets in our way here by being move only. I see th=
is design choice as a shortcoming, because it reduces the usefulness of pro=
pagate_const in many dynamic polymorphism use cases where the underlying po=
inter type would have done just fine. For example, without shallow copy ope=
rations...<br><ul><li>You cannot let the compiler implement a shallow copy =
constructor for you using "=3D default"<br></li><li>You cannot us=
e std::copy_if to find objects matching some predicate in an internal datas=
et</li><li>You cannot easily build object search acceleration structures, s=
uch as hashmaps or AABBs</li><li>You cannot easily share a reference to an =
object wrapped by propagate_const with other objects</li></ul><p>I am aware=
that shallow copies are possible using the get() and get_underlying() oper=
ations. I do not see this as a satisfactory answer, because it breaks every=
STL-ish algorithm that expects a copy constructor. Of course, I could buil=
d a propagate_const wrapper that has a copy constructor myself, and this is=
what I would end up doing if propagate_const were accepted in the STL in i=
ts current form. But I think that the use cases that I mentioned above are =
valid enough to warrant changing the design of propagate_const instead.</p>=
</div></blockquote><blockquote class=3D"gmail_quote" style=3D"margin:0px 0p=
x 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border=
-left-style:solid;padding-left:1ex"><div dir=3D"ltr"><p></p><p>I am aware t=
hat in order to be const-correct, a shallow copy constructor for propagate_=
const would need to operate from a non-const reference. I am fine with this=
tradeoff. The main reason we usually allow ourselves to make mutable copie=
s from const objects is because we assume the copy to be independent from t=
he original object. This assumption is broken when making shallow copies th=
rough pointer or reference types. Sure, many APIs will be broken initially,=
but that is unavoidable when fixing old programming language flaws. Bugs w=
ill be reported, and interfaces will be fixed, the way it's always been=
done.<br></p><p><br></p><p>For prior art, if you look at the STL's doc=
umentation, you will find that customizable algorithms that copy const data=
, such as std::copy_if, explicitly do NOT require the user-provided functio=
ns to consume the data by const-reference. So the idea of copying from non-=
const is not new. The C++ community only needs to re-discover it.<br></p><p=
><br></p></div></blockquote><div><br></div><div>We considered making copies=
from non-const propagate_const legal but were strongly dissuaded by 3 majo=
r standard library implementers.</div><div>=C2=A0=C2=A0</div><blockquote cl=
ass=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left-width:1px=
;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1e=
x"><div dir=3D"ltr"><p></p><p>I am also aware of the objection, made in a t=
hread linked above, that "We were not confident that a non-const copy =
constructor would protect=20
the user from accidental loss of const and accidental mutable shared=20
state". As far as I'm concerned, this hand-waving statement was no=
t properly justified. From my perspective, the proposed alternative of usin=
g get_underlying seems to be much bigger breach of const-correctness than a=
copy constructor that operates from mutable, because from a semantic point=
of view, get_underlying is essentially a silent const_cast that standard c=
ode analysis tools won't catch.</p><p><br></p></div></blockquote><div><=
br></div><div>We added `get_underlying` as an nicer alternative to `reinter=
pret_cast`, which would give access to the underlying pointer.<br></div><di=
v><br></div><div>Our design sought to avoid silent surprise. Granted a user=
can do the wrong thing with get_underlying but the code is written by the =
user, not by the compiler. Forcing the user to be explicit offers some prot=
ection.</div><div>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"ma=
rgin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,=
204);border-left-style:solid;padding-left:1ex"><div dir=3D"ltr"><p></p><br>=
<font size=3D"4">Const-correct convertibility to the underlying pointer typ=
e</font><br><br>Sometimes, one has to extract a pointer back from the propa=
gate_const wrapper. I would see two main use cases for this:<br><ul><li>To =
feed a legacy API that is not (yet?) compatible with propagate_const.<br></=
li><li>To turn a propagate_const<T> into a pointer-to-const, when the=
recipient should not be able to modify to the target object</li></ul><p>Th=
e current propagate_const interface provides three ways to do this:</p><ul>=
<li>Call get() and get a raw pointer</li><li>Rely on an implicit cast to ra=
w pointer, if available</li><li>Use get_underlying to access the internals =
of propagate_const.</li></ul><p>As I'm going to elaborate, the first tw=
o operations are not always the right tool for the job at hand, because the=
y fail to convey important ownership information from the underlying pointe=
r type. Whereas get_underlying, as currently implemented, is a flawed inter=
face that goes directly against the design goals of propagate_const and sho=
uld be eradicated before standardization.</p><p><br></p><p>As a use case, s=
uppose that as discussed previously, I am working a "slave object"=
;, which holds a shared_ptr to an associated "master object" that=
it shares with other slaves. To improve the const-correctness of this desi=
gn, I have decided to refactor the shared_ptr into a propagate_const<sha=
red_ptr>. So far, so good.<br></p><p><br></p><p></p><div style=3D"backgr=
ound-color:rgb(250,250,250);border:1px solid rgb(187,187,187)" class=3D"gma=
il-m_-1550785340728421304prettyprint"><code class=3D"gmail-m_-1550785340728=
421304prettyprint"><div class=3D"gmail-m_-1550785340728421304subprettyprint=
"><span style=3D"color:rgb(0,0,136)" class=3D"gmail-m_-1550785340728421304s=
tyled-by-prettify">class</span><span style=3D"color:rgb(0,0,0)" class=3D"gm=
ail-m_-1550785340728421304styled-by-prettify"> </span><span style=3D"color:=
rgb(102,0,102)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">Ma=
ster</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728=
421304styled-by-prettify"><br></span><span style=3D"color:rgb(102,102,0)" c=
lass=3D"gmail-m_-1550785340728421304styled-by-prettify">{</span><span style=
=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-pretti=
fy"><br>=C2=A0 </span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m=
_-1550785340728421304styled-by-prettify">...</span><span style=3D"color:rgb=
(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"><br></spa=
n><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-155078534072842130=
4styled-by-prettify">};</span><span style=3D"color:rgb(0,0,0)" class=3D"gma=
il-m_-1550785340728421304styled-by-prettify"><br><br></span><span style=3D"=
color:rgb(0,0,136)" class=3D"gmail-m_-1550785340728421304styled-by-prettify=
">class</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340=
728421304styled-by-prettify"> </span><span style=3D"color:rgb(102,0,102)" c=
lass=3D"gmail-m_-1550785340728421304styled-by-prettify">Slave</span><span s=
tyle=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-pr=
ettify"><br></span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1=
550785340728421304styled-by-prettify">{</span><span style=3D"color:rgb(0,0,=
0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"><br></span><sp=
an style=3D"color:rgb(0,0,136)" class=3D"gmail-m_-1550785340728421304styled=
-by-prettify">private</span><span style=3D"color:rgb(102,102,0)" class=3D"g=
mail-m_-1550785340728421304styled-by-prettify">:</span><span style=3D"color=
:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"><br>=
=C2=A0 </span><span style=3D"color:rgb(136,0,0)" class=3D"gmail-m_-15507853=
40728421304styled-by-prettify">// This was refactored from "std::share=
d_ptr<Master> m_master"</span><span style=3D"color:rgb(0,0,0)" c=
lass=3D"gmail-m_-1550785340728421304styled-by-prettify"><br>=C2=A0 std</spa=
n><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-155078534072842130=
4styled-by-prettify">::</span><span style=3D"color:rgb(0,0,0)" class=3D"gma=
il-m_-1550785340728421304styled-by-prettify">propagate_const</span><span st=
yle=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-by=
-prettify"><</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-15=
50785340728421304styled-by-prettify">std</span><span style=3D"color:rgb(102=
,102,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">::</span>=
<span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304style=
d-by-prettify">shar<wbr>ed_ptr</span><span style=3D"color:rgb(102,102,0)" c=
lass=3D"gmail-m_-1550785340728421304styled-by-prettify"><</span><span st=
yle=3D"color:rgb(102,0,102)" class=3D"gmail-m_-1550785340728421304styled-by=
-prettify">Master</span><span style=3D"color:rgb(102,102,0)" class=3D"gmail=
-m_-1550785340728421304styled-by-prettify">>></span><span style=3D"co=
lor:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"> m=
_master</span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-155078=
5340728421304styled-by-prettify">;</span><span style=3D"color:rgb(0,0,0)" c=
lass=3D"gmail-m_-1550785340728421304styled-by-prettify"><br><br></span><spa=
n style=3D"color:rgb(0,0,136)" class=3D"gmail-m_-1550785340728421304styled-=
by-prettify">public</span><span style=3D"color:rgb(102,102,0)" class=3D"gma=
il-m_-1550785340728421304styled-by-prettify">:</span><span style=3D"color:r=
gb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"><br>=C2=
=A0 </span><span style=3D"color:rgb(102,0,102)" class=3D"gmail-m_-155078534=
0728421304styled-by-prettify">Slave</span><span style=3D"color:rgb(102,102,=
0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">(</span><span =
style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-p=
rettify"> std</span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-=
1550785340728421304styled-by-prettify">::</span><span style=3D"color:rgb(0,=
0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">shared_ptr</=
span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-155078534072842=
1304styled-by-prettify"><</span><span style=3D"color:rgb(102,0,102)" cla=
ss=3D"gmail-m_-1550785340728421304styled-by-prettify">Master</span><span st=
yle=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-by=
-prettify">></span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-15=
50785340728421304styled-by-prettify"> master </span><span style=3D"color:rg=
b(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">)</s=
pan><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304s=
tyled-by-prettify"><br>=C2=A0 =C2=A0 </span><span style=3D"color:rgb(102,10=
2,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">:</span><spa=
n style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by=
-prettify"> m_master</span><span style=3D"color:rgb(102,102,0)" class=3D"gm=
ail-m_-1550785340728421304styled-by-prettify">(</span><span style=3D"color:=
rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">master=
</span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728=
421304styled-by-prettify">)</span><span style=3D"color:rgb(0,0,0)" class=3D=
"gmail-m_-1550785340728421304styled-by-prettify"><br>=C2=A0 </span><span st=
yle=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-by=
-prettify">{</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-15507=
85340728421304styled-by-prettify"><br>=C2=A0 =C2=A0 m_master</span><span st=
yle=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-by=
-prettify">-></span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1=
550785340728421304styled-by-prettify">addSlave</span><span style=3D"color:r=
gb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">(</=
span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304=
styled-by-prettify"> </span><span style=3D"color:rgb(102,102,0)" class=3D"g=
mail-m_-1550785340728421304styled-by-prettify">*</span><span style=3D"color=
:rgb(0,0,136)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">thi=
s</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421=
304styled-by-prettify"> </span><span style=3D"color:rgb(102,102,0)" class=
=3D"gmail-m_-1550785340728421304styled-by-prettify">);</span><span style=3D=
"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"=
><br>=C2=A0 </span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1=
550785340728421304styled-by-prettify">}</span><span style=3D"color:rgb(0,0,=
0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"><br><br>=C2=A0=
...<br></span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-15507=
85340728421304styled-by-prettify">};</span><span style=3D"font-family:arial=
,sans-serif;background-color:rgb(255,255,255)">=C2=A0</span></div></code></=
div></div></blockquote><blockquote class=3D"gmail_quote" style=3D"margin:0p=
x 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);bo=
rder-left-style:solid;padding-left:1ex"><div dir=3D"ltr"><p></p>But as I pr=
oceed with the refactoring, I discover that the Slave class used to provide=
a method that shares access to its master:<br><br><div style=3D"background=
-color:rgb(250,250,250);border:1px solid rgb(187,187,187)" class=3D"gmail-m=
_-1550785340728421304prettyprint"><code class=3D"gmail-m_-15507853407284213=
04prettyprint"><div class=3D"gmail-m_-1550785340728421304subprettyprint"><s=
pan style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-=
by-prettify">std</span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-=
m_-1550785340728421304styled-by-prettify">::</span><span style=3D"color:rgb=
(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">shared_pt=
r</span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-155078534072=
8421304styled-by-prettify"><</span><span style=3D"color:rgb(102,0,102)" =
class=3D"gmail-m_-1550785340728421304styled-by-prettify">Master</span><span=
style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled=
-by-prettify">></span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_=
-1550785340728421304styled-by-prettify"> </span><span style=3D"color:rgb(10=
2,0,102)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">Slave</s=
pan><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421=
304styled-by-prettify">::</span><span style=3D"color:rgb(0,0,0)" class=3D"g=
mail-m_-1550785340728421304styled-by-prettify">getMaster</span><span style=
=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-by-pr=
ettify">()</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785=
340728421304styled-by-prettify"> </span><span style=3D"color:rgb(0,0,136)" =
class=3D"gmail-m_-1550785340728421304styled-by-prettify">const</span><span =
style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-p=
rettify"><br></span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-=
1550785340728421304styled-by-prettify">{</span><span style=3D"color:rgb(0,0=
,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"><br>=C2=A0 </=
span><span style=3D"color:rgb(0,0,136)" class=3D"gmail-m_-15507853407284213=
04styled-by-prettify">return</span><span style=3D"color:rgb(0,0,0)" class=
=3D"gmail-m_-1550785340728421304styled-by-prettify"> m_master</span><span s=
tyle=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-b=
y-prettify">;</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550=
785340728421304styled-by-prettify"><br></span><span style=3D"color:rgb(102,=
102,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">}</span></=
div></code></div><br>This does not compile anymore. And I'm happy about=
that: it should never have compiled to begin with. Returning non-const acc=
ess to my members from a const method definitely does not match my idea of =
const-correctness!<br><br>Instead, I would like to only provide const acces=
s to the master object, like so:<br><br><div style=3D"background-color:rgb(=
250,250,250);border:1px solid rgb(187,187,187)" class=3D"gmail-m_-155078534=
0728421304prettyprint"><code class=3D"gmail-m_-1550785340728421304prettypri=
nt"><div class=3D"gmail-m_-1550785340728421304subprettyprint"><span style=
=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-pretti=
fy">std</span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-155078=
5340728421304styled-by-prettify">::</span><span style=3D"color:rgb(0,0,0)" =
class=3D"gmail-m_-1550785340728421304styled-by-prettify">shared_ptr</span><=
span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304st=
yled-by-prettify"><</span><span style=3D"color:rgb(0,0,136)" class=3D"gm=
ail-m_-1550785340728421304styled-by-prettify">const</span><span style=3D"co=
lor:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"> <=
/span><span style=3D"color:rgb(102,0,102)" class=3D"gmail-m_-15507853407284=
21304styled-by-prettify">Master</span><span style=3D"color:rgb(102,102,0)" =
class=3D"gmail-m_-1550785340728421304styled-by-prettify">></span><span s=
tyle=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-pr=
ettify"> </span><span style=3D"color:rgb(102,0,102)" class=3D"gmail-m_-1550=
785340728421304styled-by-prettify">Slave</span><span style=3D"color:rgb(102=
,102,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">::</span>=
<span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304style=
d-by-prettify">getMaster</span><span style=3D"color:rgb(102,102,0)" class=
=3D"gmail-m_-1550785340728421304styled-by-prettify">()</span><span style=3D=
"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"=
> </span><span style=3D"color:rgb(0,0,136)" class=3D"gmail-m_-1550785340728=
421304styled-by-prettify">const</span><span style=3D"color:rgb(102,102,0)" =
class=3D"gmail-m_-1550785340728421304styled-by-prettify">;</span></div></co=
de></div><br>If my clients are well-behaved and do not mutate the master ob=
ject, this will be a minimally invasive interface change. It will require o=
nly minor client rewrites, the kind that could be automated by sed or an ID=
E. It could even require no client rewrite at all if I end up being lucky a=
nd have clients that were using auto and friends.<br><br>Unfortunately, the=
current propagate_const interface does not allow me to implement this meth=
od in a clean way.<br><br></div></blockquote><div><br></div><div>Sadly that=
's intended. We can't guarantee that "clients are well-behaved=
and do not mutate the master object".</div><div>=C2=A0</div><blockquo=
te class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left-widt=
h:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-le=
ft:1ex"><div dir=3D"ltr">The obvious code snippet would not work due to the=
lack of an appropriate implicit conversion:<br><br><div style=3D"backgroun=
d-color:rgb(250,250,250);border:1px solid rgb(187,187,187)" class=3D"gmail-=
m_-1550785340728421304prettyprint"><code class=3D"gmail-m_-1550785340728421=
304prettyprint"><div class=3D"gmail-m_-1550785340728421304subprettyprint"><=
span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled=
-by-prettify">std</span><span style=3D"color:rgb(102,102,0)" class=3D"gmail=
-m_-1550785340728421304styled-by-prettify">::</span><span style=3D"color:rg=
b(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">shared_p=
tr</span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-15507853407=
28421304styled-by-prettify"><const </span><span style=3D"color:rgb(102,0=
,102)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">Master</spa=
n><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-155078534072842130=
4styled-by-prettify">></span><span style=3D"color:rgb(0,0,0)" class=3D"g=
mail-m_-1550785340728421304styled-by-prettify"> </span><span style=3D"color=
:rgb(102,0,102)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">S=
lave</span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-155078534=
0728421304styled-by-prettify">::</span><span style=3D"color:rgb(0,0,0)" cla=
ss=3D"gmail-m_-1550785340728421304styled-by-prettify">getMaster</span><span=
style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled=
-by-prettify">()</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1=
550785340728421304styled-by-prettify"> </span><span style=3D"color:rgb(0,0,=
136)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">const</span>=
<span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304style=
d-by-prettify"><br></span><span style=3D"color:rgb(102,102,0)" class=3D"gma=
il-m_-1550785340728421304styled-by-prettify">{</span><span style=3D"color:r=
gb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"><br>=C2=
=A0 </span><span style=3D"color:rgb(0,0,136)" class=3D"gmail-m_-15507853407=
28421304styled-by-prettify">return</span><span style=3D"color:rgb(0,0,0)" c=
lass=3D"gmail-m_-1550785340728421304styled-by-prettify"> m_master</span><sp=
an style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styl=
ed-by-prettify">;</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-=
1550785340728421304styled-by-prettify">=C2=A0 // ERROR: No implicit convers=
ion from propagate_const<shared_ptr>!<br></span><span style=3D"color:=
rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">}<=
/span></div></code></div><br>Using get() here would be the perfect way to i=
ntroduce use after free bugs:<br><br><div style=3D"background-color:rgb(250=
,250,250);border:1px solid rgb(187,187,187)" class=3D"gmail-m_-155078534072=
8421304prettyprint"><code class=3D"gmail-m_-1550785340728421304prettyprint"=
><div class=3D"gmail-m_-1550785340728421304subprettyprint"><span style=3D"c=
olor:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">s=
td</span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-15507853407=
28421304styled-by-prettify">::</span><span style=3D"color:rgb(0,0,0)" class=
=3D"gmail-m_-1550785340728421304styled-by-prettify">shared_ptr</span><span =
style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-=
by-prettify"><const </span><span style=3D"color:rgb(102,0,102)" class=3D=
"gmail-m_-1550785340728421304styled-by-prettify">Master</span><span style=
=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-by-pr=
ettify">></span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-15507=
85340728421304styled-by-prettify"> </span><span style=3D"color:rgb(102,0,10=
2)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">Slave</span><s=
pan style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304sty=
led-by-prettify">::</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m=
_-1550785340728421304styled-by-prettify">getMaster</span><span style=3D"col=
or:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"=
>()</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-15507853407284=
21304styled-by-prettify"> </span><span style=3D"color:rgb(0,0,136)" class=
=3D"gmail-m_-1550785340728421304styled-by-prettify">const</span><span style=
=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-pretti=
fy"><br></span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-15507=
85340728421304styled-by-prettify">{</span><span style=3D"color:rgb(0,0,0)" =
class=3D"gmail-m_-1550785340728421304styled-by-prettify"><br>=C2=A0 const M=
aster* master =3D m_master.get();<br>=C2=A0 </span><span style=3D"color:rgb=
(0,0,136)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">return<=
/span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-155078534072842130=
4styled-by-prettify"> std::shared_ptr<Master>( master</span><span sty=
le=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-by-=
prettify"> );</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550=
785340728421304styled-by-prettify">=C2=A0 // INCORRECT: Two owners for one =
object!<br></span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-15=
50785340728421304styled-by-prettify">}</span></div></code></div><br>And usi=
ng get_underlying will both break const correctness and fail to provide me =
with the correct return type:<br><br><div style=3D"background-color:rgb(250=
,250,250);border:1px solid rgb(187,187,187)" class=3D"gmail-m_-155078534072=
8421304prettyprint"><code class=3D"gmail-m_-1550785340728421304prettyprint"=
><div class=3D"gmail-m_-1550785340728421304subprettyprint"><span style=3D"c=
olor:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">s=
td</span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-15507853407=
28421304styled-by-prettify">::</span><span style=3D"color:rgb(0,0,0)" class=
=3D"gmail-m_-1550785340728421304styled-by-prettify">shared_ptr</span><span =
style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-=
by-prettify"><const </span><span style=3D"color:rgb(102,0,102)" class=3D=
"gmail-m_-1550785340728421304styled-by-prettify">Master</span><span style=
=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-by-pr=
ettify">></span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-15507=
85340728421304styled-by-prettify"> </span><span style=3D"color:rgb(102,0,10=
2)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">Slave</span><s=
pan style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304sty=
led-by-prettify">::</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m=
_-1550785340728421304styled-by-prettify">getMaster</span><span style=3D"col=
or:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"=
>()</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-15507853407284=
21304styled-by-prettify"> </span><span style=3D"color:rgb(0,0,136)" class=
=3D"gmail-m_-1550785340728421304styled-by-prettify">const</span><span style=
=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-pretti=
fy"><br></span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-15507=
85340728421304styled-by-prettify">{</span><span style=3D"color:rgb(0,0,0)" =
class=3D"gmail-m_-1550785340728421304styled-by-prettify"><br>=C2=A0 auto ma=
ster =3D std::get_underlying( m_master );=C2=A0 // This is an std::shared_p=
tr<Master><br>=C2=A0 </span><span style=3D"color:rgb(0,0,136)" class=
=3D"gmail-m_-1550785340728421304styled-by-prettify">return</span><span styl=
e=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prett=
ify"> master;=C2=A0 // ERROR: No implicit conversion to std::shared_ptr<=
const Master>!</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-=
1550785340728421304styled-by-prettify"><br></span><span style=3D"color:rgb(=
102,102,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">}</spa=
n></div></code></div><br>To implement this method, in addition to the const=
-incorrect get_underlying interface, I would also need a const cast!<br><br=
><div style=3D"background-color:rgb(250,250,250);border:1px solid rgb(187,1=
87,187)" class=3D"gmail-m_-1550785340728421304prettyprint"><code class=3D"g=
mail-m_-1550785340728421304prettyprint"><div class=3D"gmail-m_-155078534072=
8421304subprettyprint"><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1=
550785340728421304styled-by-prettify">std</span><span style=3D"color:rgb(10=
2,102,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">::</span=
><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styl=
ed-by-prettify">shared_ptr</span><span style=3D"color:rgb(102,102,0)" class=
=3D"gmail-m_-1550785340728421304styled-by-prettify"><const </span><span =
style=3D"color:rgb(102,0,102)" class=3D"gmail-m_-1550785340728421304styled-=
by-prettify">Master</span><span style=3D"color:rgb(102,102,0)" class=3D"gma=
il-m_-1550785340728421304styled-by-prettify">></span><span style=3D"colo=
r:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"> </s=
pan><span style=3D"color:rgb(102,0,102)" class=3D"gmail-m_-1550785340728421=
304styled-by-prettify">Slave</span><span style=3D"color:rgb(102,102,0)" cla=
ss=3D"gmail-m_-1550785340728421304styled-by-prettify">::</span><span style=
=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-pretti=
fy">getMaster</span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-=
1550785340728421304styled-by-prettify">()</span><span style=3D"color:rgb(0,=
0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"> </span><spa=
n style=3D"color:rgb(0,0,136)" class=3D"gmail-m_-1550785340728421304styled-=
by-prettify">const</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_=
-1550785340728421304styled-by-prettify"><br></span><span style=3D"color:rgb=
(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">{</sp=
an><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304st=
yled-by-prettify"><br>=C2=A0 auto master =3D std::get_underlying( m_master =
); <br>=C2=A0 </span><span style=3D"color:rgb(0,0,136)" class=3D"gmail-m_-1=
550785340728421304styled-by-prettify">return</span><span style=3D"color:rgb=
(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"> std::con=
st_pointer_cast<const Master>( master );</span><span style=3D"color:r=
gb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"><br></s=
pan><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421=
304styled-by-prettify">}</span></div></code></div><p><br></p><p>For sure, I=
would never want an abomination like this to pass code review :)</p><p><br=
></p></div></blockquote><div>Agreed.</div><div>=C2=A0</div><blockquote clas=
s=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left-width:1px;b=
order-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"=
><div dir=3D"ltr"><p></p><p>This little thought experiment showcases two ma=
jor issues with get_underlying:</p><ul><li>Counter to the goal of of propag=
ate_const, it makes it trivial to violate const-correctness without the com=
piler noticing (or, for that matter, any developer or static analysis tool =
that is unfamiliar with the propagate_const API).<br></li><li>By not return=
ing the const-correct type, get_underlying can make it unnecessarily hard t=
o implement const-correct interfaces.<br></li></ul><p></p><p>The authors of=
the original propagate_const proposal were aware that the get_underlying i=
nterface was suboptimal, and for this reason they opted to make it less usa=
ble by hiding it as a free function instead of making it a proper method of=
propagate_const. But personnally, I would go further: drop get_underlying =
altogether, and replace it with something that is both const-correct and re=
spectful of pointer ownership issues.<br></p><p><br></p><p>My counter-propo=
sal would be to have a propagate_const method, maybe called "share()&q=
uot;, which enables sharing access to the data pointed by propagate_const w=
ith due respect paid to both const-correctness and the underlying pointer&#=
39;s ownership semantics. In practice:</p><ul><li>If called on propagate_co=
nst<T*>, share() should return a T*</li><li>If called on const propag=
ate_const<T*>, share() should return a const T*</li><li>If called on =
propagate_const<shared_ptr<T>><wbr>, share() should return a sh=
ared_ptr<T></li><li>If called on const propagate_const<shared_ptr&=
lt;T>><wbr>, share() should return a shared_ptr<const T></li><l=
i>share() should not be defined on propagate_const<unique_ptr<T>&g=
t; since that pointer does not support sharing ownership</li></ul><p>In add=
ition, as a convenience, whenever a share() method exists, an implicit cast=
could be provided to the underlying pointer type as a way to make such own=
ership sharing more convenient and to enable things like comparison of cont=
ainers of shared_ptr<T> with containers of propagate_const<shared_=
ptr<T>> through standard (STL-ish) algorithms.</p><p><br></p><p>If=
you really, positively want something like get_underlying to exist, it sho=
uld only be applicable to non-const object. This alone would make it const-=
correct, and thus remove the need to implement it as a free function and be=
careful when using it.</p><p><br></p><p>With this simple change, shameless=
people who want to get their hands dirty and extract a mutable pointer fro=
m a const wrapper will then need to do some extra work, as should be expect=
ed of them in my opinion:</p><p><br></p><div style=3D"background-color:rgb(=
250,250,250);border:1px solid rgb(187,187,187)" class=3D"gmail-m_-155078534=
0728421304prettyprint"><code class=3D"gmail-m_-1550785340728421304prettypri=
nt"><div class=3D"gmail-m_-1550785340728421304subprettyprint"><span style=
=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-pretti=
fy"></span><span style=3D"color:rgb(0,0,136)" class=3D"gmail-m_-15507853407=
28421304styled-by-prettify">using</span><span style=3D"color:rgb(0,0,0)" cl=
ass=3D"gmail-m_-1550785340728421304styled-by-prettify"> </span><span style=
=3D"color:rgb(102,0,102)" class=3D"gmail-m_-1550785340728421304styled-by-pr=
ettify">ConstPropagator</span><span style=3D"color:rgb(0,0,0)" class=3D"gma=
il-m_-1550785340728421304styled-by-prettify"> </span><span style=3D"color:r=
gb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">=3D=
</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-15507853407284213=
04styled-by-prettify"> std</span><span style=3D"color:rgb(102,102,0)" class=
=3D"gmail-m_-1550785340728421304styled-by-prettify">::</span><span style=3D=
"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"=
>propagate_const</span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-=
m_-1550785340728421304styled-by-prettify"><</span><span style=3D"color:r=
gb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">std</sp=
an><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-15507853407284213=
04styled-by-prettify">::</span><span style=3D"color:rgb(0,0,0)" class=3D"gm=
ail-m_-1550785340728421304styled-by-prettify">shar<wbr>ed_ptr</span><span s=
tyle=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-b=
y-prettify"><</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1=
550785340728421304styled-by-prettify">T</span><span style=3D"color:rgb(102,=
102,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">>>;<=
/span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-155078534072842130=
4styled-by-prettify"><br><br></span><span style=3D"color:rgb(0,0,136)" clas=
s=3D"gmail-m_-1550785340728421304styled-by-prettify">const</span><span styl=
e=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prett=
ify"> </span><span style=3D"color:rgb(102,0,102)" class=3D"gmail-m_-1550785=
340728421304styled-by-prettify">ConstPropagator</span><span style=3D"color:=
rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">&a=
mp;</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-15507853407284=
21304styled-by-prettify"> immutableRef </span><span style=3D"color:rgb(102,=
102,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">=3D</span>=
<span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304style=
d-by-prettify"> </span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-=
m_-1550785340728421304styled-by-prettify">...</span><span style=3D"color:rg=
b(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"> </span>=
<span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304s=
tyled-by-prettify">;</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-=
m_-1550785340728421304styled-by-prettify"><br>std</span><span style=3D"colo=
r:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">=
::</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-155078534072842=
1304styled-by-prettify">shared_ptr</span><span style=3D"color:rgb(102,102,0=
)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"><</span><spa=
n style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-1550785340728421304styled-by=
-prettify">T</span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1=
550785340728421304styled-by-prettify">></span><span style=3D"color:rgb(0=
,0,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"> mutableRef=
</span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-155078534072=
8421304styled-by-prettify">=3D</span><span style=3D"color:rgb(0,0,0)" class=
=3D"gmail-m_-1550785340728421304styled-by-prettify"> </span><span style=3D"=
color:rgb(0,0,136)" class=3D"gmail-m_-1550785340728421304styled-by-prettify=
">const_cast</span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1=
550785340728421304styled-by-prettify"><</span><span style=3D"color:rgb(1=
02,0,102)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">ConstPr=
opagator</span><span style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-15507=
85340728421304styled-by-prettify">*>(</span><span style=3D"color:rgb(0,0=
,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify"> </span><span=
style=3D"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled=
-by-prettify">&</span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m=
_-1550785340728421304styled-by-prettify">immutableRef </span><span style=3D=
"color:rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-by-prett=
ify">)-></span><span style=3D"color:rgb(0,0,0)" class=3D"gmail-m_-155078=
5340728421304styled-by-prettify">get_underlying</span><span style=3D"color:=
rgb(102,102,0)" class=3D"gmail-m_-1550785340728421304styled-by-prettify">()=
;</span></div></code></div></div><span class=3D"gmail-HOEnZb"><font color=
=3D"#888888">
<p></p>
-- <br>
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br>
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org" target=3D"_=
blank">std-proposals+unsubscribe@<wbr>isocpp.org</a>.<br>
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org" target=3D"_blank">std-proposals@isocpp.org</a>.<br>
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/9ad3f22a-a619-4488-bf1e-303641c968b9%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter" target=3D"_blank">=
https://groups.google.com/a/<wbr>isocpp.org/d/msgid/std-<wbr>proposals/9ad3=
f22a-a619-4488-<wbr>bf1e-303641c968b9%40isocpp.org</a><wbr>.<br>
</font></span></blockquote></div><br></div></div>
<p></p>
-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/CAAbBDD8Xw09taeNMuGtpaozWsDCo68y8TOjA=
Xk%2BhfNanJi_XZg%40mail.gmail.com?utm_medium=3Demail&utm_source=3Dfooter">h=
ttps://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAAbBDD8Xw09tae=
NMuGtpaozWsDCo68y8TOjAXk%2BhfNanJi_XZg%40mail.gmail.com</a>.<br />
--94eb2c190d14e1a6f905491aaaff--
.
Author: Hadrien Grasland <hadrien.grasland@gmail.com>
Date: Fri, 24 Feb 2017 08:06:08 -0800 (PST)
Raw View
------=_Part_274_1515010948.1487952368943
Content-Type: multipart/alternative;
boundary="----=_Part_275_170315023.1487952368946"
------=_Part_275_170315023.1487952368946
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
Le mercredi 22 f=C3=A9vrier 2017 09:56:10 UTC+1, Jonathan Coe a =C3=A9crit =
:
>
>
> On 21 February 2017 at 14:54, Hadrien Grasland wrote:
>
>> Hi everyone,
>>
>> I am not sure what this group's policy is regarding resurrection of old=
=20
>> threads, so I have decided to create a new one. But I would like to seco=
nd=20
>> two comments that were previously made on the propagate_const proposal o=
f=20
>> N4617:
>>
>> 1/ That it should be possible to copy a propagate_const<T> wrapper (=20
>> https://groups.google.com/a/isocpp.org/forum/#!searchin/std-proposals/pr=
opagate_const/std-proposals/1uDKcA9bssU/AZ-aQNAZAAAJ=20
>> ).
>> 2/ That propagate_const<T> should be convertible to a const-correct form=
=20
>> of the underlying pointer type (=20
>> https://groups.google.com/a/isocpp.org/forum/#!searchin/std-proposals/pr=
opagate_const/std-proposals/7rSMtvQVASk/niLSKtkcBwAJ=20
>> ).
>>
>> Let me motivate, through some concrete use cases, why I think that both=
=20
>> of these changes are necessary.
>>
>>
>> Need for shallow copy operations
>>
>> There are many reasons to use pointers in C++. A very common use case,=
=20
>> however, is dynamic polymorphism:
>>
>> - Different implementations of a virtual interface may have different=
=20
>> sizes
>> - C++, in its commendable quest for zero-cost abstraction, does not=
=20
>> provide native support for dynamically sized types
>> - When combined, these two language design choices lead to the=20
>> conclusion that if the set of implementations of an interface is not =
known=20
>> as compile time, polymorphism intrinsically requires pointer indirect=
ion in=20
>> C++
>>
>> For this use case, pointer constness semantics are inadequate. If you us=
e=20
>> a pointer-to-const, you cannot modify the target object, which is often=
=20
>> fine but tends to be excessively limitating for some use cases,=20
>> particularly when implementing constructors and factory functions. If, o=
n=20
>> the other hand, you use a pointer-to-mutable, you open a const-correctne=
ss=20
>> hole by allowing yourself to mutate a const object without any compiler=
=20
>> warning. Quite frankly, none of these options are very appealing.
>>
>> // Pointer-to-const restricts some valid use cases of polymorphism
>> class FirstTry
>> {
>> private:
>> std::unique_ptr<const Interface> m_ptr;
>>
>> public:
>> FirstTry()
>> : m_ptr{ std::make_unique<Implementation>( ... ) }
>> {
>> m_ptr->setParent( *this ); // ERROR: Can't mutate through a const=
=20
>> pointer!
>> }
>> };
>>
>>
>> // Pointer-to-mutable breaks const correctness of polymorphism
>> class SecondTry
>> {
>> private:
>> std::unique_ptr<Interface> m_ptr;
>>
>> public:
>> SecondTry()
>> : m_ptr{ std::make_unique<Implementation>( ... ) }
>> {
>> m_ptr->setParent( *this ); // This is now okay...
>> }
>>
>> int someRandomAccessor() const {
>> m_ptr->setSomething(); // ...but unfortunately, this is okay too :-=
(
>> return 42;
>> }
>> };
>>
>> Now, if we are on the same wavelength, and I will assume in the followin=
g=20
>> that this is the case, this is precisely the kind of issue that=20
>> propagate_const is designed to help with.
>>
>> Let us now turn our attention to copy operations. Ideally, polymorphic=
=20
>> objects should be deep-copyable, just like any regular value type.=20
>> Unfortunately, in another application of the zero cost abstraction=20
>> principle, C++ provides no easy way to do this. Anyone who wants to comb=
ine=20
>> value semantics and dynamic polymorphism needs to provide explicit suppo=
rt=20
>> for deep copies across the entire class hierarchy, which is cumbersome t=
o=20
>> begin with and can get problematic when interacting with third-party=20
>> libraries.
>>
>
> Arthur has addressed your concerns with the same points I would have=20
> raised (thanks Arthur). On the related topic of deep-copies and=20
> const-propagation we're working on polymorphic_value which aims to tackle=
=20
> copies through class heirarchies. I'd be interested if you have a use cas=
e=20
> where one of propagate_const and polymorphic_value does not solve your=20
> problem.=20
>
>
> https://github.com/jbcoe/polymorphic_value/blob/master/talks/2017_1_25_cx=
x_london.md
> https://github.com/jbcoe/polymorphic_value/blob/master/draft.md
>
I love the idea of polymorphic_value. I think that once available, it=20
should be taught to new C++ programmers as the standard way to manipulate=
=20
polymorphic objects, and that anyone should strongly consider using it for=
=20
this purpose in new codebases, or whenever a mature codebase gets=20
completely rewritten.
One thing which I do not think polymorphic_value can do, however, is be=20
gradually introduced in an existing codebase. By design, polymorphic_value=
=20
has to be constructed from pointer-to-derived, and is thus fundamentally=20
incompatible with the massive body of existing C++ code that uses=20
pointer-to-base for polymorphism. Once you introduce a polymorphic_value=20
somewhere, you also need to rewrite every other piece of client code that=
=20
uses the same class hierarchy, often including third-party libraries that=
=20
you may have no easy access to. Even though the final code will be cleaner,=
=20
the process will be even more costly as adding clone() methods everywhere.=
=20
And for large projects, that will just not be seen as a viable endeavour.
I believe that with the modifications that I am proposing, propagate_const=
=20
would be a better fit for such refactoring purposes. With it, one could=20
progressively introduce const-correct pointer manipulation at relatively=20
little cost. Sure, you wouldn't have deep copies, but hey, developers of=20
existing codebases never had them to begin with. So we aren't going to miss=
=20
them in a context of progressive code evolution. And if error-prone shallow=
=20
copies are harder to perform by accident, well, all the better.
=20
>> class Interface
>> {
>> public:
>> // This kind of boilerplate must be added to every class supporting=20
>> deep copy...
>> virtual std::unique_ptr<Interface> clone() const =3D 0;
>> };
>>
>> class Implementation : public Interface
>> {
>> public:
>> // ...and replicated again and again in every single non-abstract=20
>> child. No fun.
>> std::unique_ptr<Interface> clone() const final override
>> {
>> return std::make_unique<Implementation>( *this );
>> }
>> };
>>
>> But thankfully, sometimes, we can do without a true deep copy, and=20
>> satisfy ourselves with a shallow copy. And in fact, for some use cases, =
a=20
>> shallow copy will even be exactly what we want:
>>
>> - When building some variety of search acceleration structure=20
>> (hashmap, neighbour locator, axis-aligned-bounding box...)
>> - When many "slave objects" share a reference to some common,=20
>> potentially large "master object"
>>
>> Unfortunately, even though all standard C++ pointer types make it trivia=
l=20
>> to shallow-copy a polymorphic object, propagate_const gets in our way he=
re=20
>> by being move only. I see this design choice as a shortcoming, because i=
t=20
>> reduces the usefulness of propagate_const in many dynamic polymorphism u=
se=20
>> cases where the underlying pointer type would have done just fine. For=
=20
>> example, without shallow copy operations...
>>
>> - You cannot let the compiler implement a shallow copy constructor=20
>> for you using "=3D default"
>> - You cannot use std::copy_if to find objects matching some predicate=
=20
>> in an internal dataset
>> - You cannot easily build object search acceleration structures, such=
=20
>> as hashmaps or AABBs
>> - You cannot easily share a reference to an object wrapped by=20
>> propagate_const with other objects
>>
>> I am aware that shallow copies are possible using the get() and=20
>> get_underlying() operations. I do not see this as a satisfactory answer,=
=20
>> because it breaks every STL-ish algorithm that expects a copy constructo=
r.=20
>> Of course, I could build a propagate_const wrapper that has a copy=20
>> constructor myself, and this is what I would end up doing if=20
>> propagate_const were accepted in the STL in its current form. But I thin=
k=20
>> that the use cases that I mentioned above are valid enough to warrant=20
>> changing the design of propagate_const instead.
>>
> I am aware that in order to be const-correct, a shallow copy constructor=
=20
>> for propagate_const would need to operate from a non-const reference. I =
am=20
>> fine with this tradeoff. The main reason we usually allow ourselves to m=
ake=20
>> mutable copies from const objects is because we assume the copy to be=20
>> independent from the original object. This assumption is broken when mak=
ing=20
>> shallow copies through pointer or reference types. Sure, many APIs will =
be=20
>> broken initially, but that is unavoidable when fixing old programming=20
>> language flaws. Bugs will be reported, and interfaces will be fixed, the=
=20
>> way it's always been done.
>>
>>
>> For prior art, if you look at the STL's documentation, you will find tha=
t=20
>> customizable algorithms that copy const data, such as std::copy_if,=20
>> explicitly do NOT require the user-provided functions to consume the dat=
a=20
>> by const-reference. So the idea of copying from non-const is not new. Th=
e=20
>> C++ community only needs to re-discover it.
>>
>>
>>
> We considered making copies from non-const propagate_const legal but were=
=20
> strongly dissuaded by 3 major standard library implementers.
>
Sure, they disagreed. But what were their argument for it? Was it just a=20
case of "the maffia said no"?
=20
> =20
>
>> I am also aware of the objection, made in a thread linked above, that "W=
e=20
>> were not confident that a non-const copy constructor would protect the u=
ser=20
>> from accidental loss of const and accidental mutable shared state". As f=
ar=20
>> as I'm concerned, this hand-waving statement was not properly justified.=
=20
>> From my perspective, the proposed alternative of using get_underlying se=
ems=20
>> to be much bigger breach of const-correctness than a copy constructor th=
at=20
>> operates from mutable, because from a semantic point of view,=20
>> get_underlying is essentially a silent const_cast that standard code=20
>> analysis tools won't catch.
>>
>>
>>
> We added `get_underlying` as an nicer alternative to `reinterpret_cast`,=
=20
> which would give access to the underlying pointer.
>
> Our design sought to avoid silent surprise. Granted a user can do the=20
> wrong thing with get_underlying but the code is written by the user, not =
by=20
> the compiler. Forcing the user to be explicit offers some protection.
>
Been there, done that. When you see all the tools that C++ and its standard=
=20
library provide to help you shoot yourself in the foot, it is always very=
=20
tempting to roll your own, thinking that surely it will do the job better.=
=20
Except usually, it doesn't. When what you want is a cast, make it a cast,=
=20
don't try to make it look nicer by burying it in custom abstractions.
Let's take a step back and think about this from an API design point of=20
view. Why makes get_underlying such an poor interface?
1/ By returning a reference to the internal pointer object, you leak=20
implementation details
2/ By leaking implementation details, you lose the ability to maintain the=
=20
single most important class invariant of propagate_const, which is that=20
type-safe accesses to a const propagate_const wrapper should not give one=
=20
write access to the internal object.
Now, I am aware that we are discussing C++ API design here. Leaking=20
implementation details and violating class invariants is okay, as long as=
=20
it can give me that 0.1% performance improvement in some very obscure use=
=20
case that someone thought about someday. One a single compiler. With the=20
right combination of flags. Until the next release. Perhaps.
But in the case of get_underlying, you could actually leak these=20
implementation details, if you really wanted to, without violating your=20
class invariants along the way. The fix is simple, really. Make=20
get_underlying a non-const method of the propagate_const object, and drop=
=20
get_underlying from const. Be serious about interface guarantees.
If your users come whining because they can't easily extract a=20
pointer-to-mutable from a const object, try to give them the talk about=20
const correctness. If they won't listen, tell them to const_cast the=20
propagate_const wrapper. But don't compromise your API just to make stupid=
=20
operations more convenient to write. It is never worth it.
=20
>
>> Const-correct convertibility to the underlying pointer type
>>
>> Sometimes, one has to extract a pointer back from the propagate_const=20
>> wrapper. I would see two main use cases for this:
>>
>> - To feed a legacy API that is not (yet?) compatible with=20
>> propagate_const.
>> - To turn a propagate_const<T> into a pointer-to-const, when the=20
>> recipient should not be able to modify to the target object
>>
>> The current propagate_const interface provides three ways to do this:
>>
>> - Call get() and get a raw pointer
>> - Rely on an implicit cast to raw pointer, if available
>> - Use get_underlying to access the internals of propagate_const.
>>
>> As I'm going to elaborate, the first two operations are not always the=
=20
>> right tool for the job at hand, because they fail to convey important=20
>> ownership information from the underlying pointer type. Whereas=20
>> get_underlying, as currently implemented, is a flawed interface that goe=
s=20
>> directly against the design goals of propagate_const and should be=20
>> eradicated before standardization.
>>
>>
>> As a use case, suppose that as discussed previously, I am working a=20
>> "slave object", which holds a shared_ptr to an associated "master object=
"=20
>> that it shares with other slaves. To improve the const-correctness of th=
is=20
>> design, I have decided to refactor the shared_ptr into a=20
>> propagate_const<shared_ptr>. So far, so good.
>>
>>
>> class Master
>> {
>> ...
>> };
>>
>> class Slave
>> {
>> private:
>> // This was refactored from "std::shared_ptr<Master> m_master"
>> std::propagate_const<std::shared_ptr<Master>> m_master;
>>
>> public:
>> Slave( std::shared_ptr<Master> master )
>> : m_master(master)
>> {
>> m_master->addSlave( *this );
>> }
>>
>> ...
>> };=20
>>
> But as I proceed with the refactoring, I discover that the Slave class=20
>> used to provide a method that shares access to its master:
>>
>> std::shared_ptr<Master> Slave::getMaster() const
>> {
>> return m_master;
>> }
>>
>> This does not compile anymore. And I'm happy about that: it should never=
=20
>> have compiled to begin with. Returning non-const access to my members fr=
om=20
>> a const method definitely does not match my idea of const-correctness!
>>
>> Instead, I would like to only provide const access to the master object,=
=20
>> like so:
>>
>> std::shared_ptr<const Master> Slave::getMaster() const;
>>
>> If my clients are well-behaved and do not mutate the master object, this=
=20
>> will be a minimally invasive interface change. It will require only mino=
r=20
>> client rewrites, the kind that could be automated by sed or an IDE. It=
=20
>> could even require no client rewrite at all if I end up being lucky and=
=20
>> have clients that were using auto and friends.
>>
>> Unfortunately, the current propagate_const interface does not allow me t=
o=20
>> implement this method in a clean way.
>>
>>
> Sadly that's intended. We can't guarantee that "clients are well-behaved=
=20
> and do not mutate the master object".
>
And you do not need to, that's my job. You give me a well-designed=20
const-correct pointer wrapper, and I'll use it to improve the=20
const-correctness of the codebase that I'm responsible of. If I find broken=
=20
client code, I will do my best to fix it myself, and send an angry e-mail=
=20
to the person responsible if I don't manage. It's called maintenance.
But when I'm going through the pain of doing this, I would like to do so=20
using quality tools that give me the impression that the effort is=20
worthwhile, and won't be undermined on the day after by someone trying to=
=20
do something stupid, going through the STL documentation and thinking "oh,=
=20
I know, I'll just use get_underlying...".
When people start to break the thread-safety of our code by introducing=20
mutation in places where it doesn't belong, I want them to explicitly write=
=20
down the dreaded word "cast" and feel that chill going down their spine,=20
telling them that they're doing something wrong. I want them to figure out=
=20
that the basic design of their code is broken, and to understand that they=
=20
need to fix it before submitting it upstream. I don't want the only line of=
=20
defense against software chaos to be me telling them at the end, when they=
=20
finally submit their merge request, that I can't accept it into our=20
codebase and they need to rewrite half of it.
Good interfaces save everyone's time by making bad things look bad. Let's=
=20
have more of them in C++.
=20
> =20
>
>> The obvious code snippet would not work due to the lack of an appropriat=
e=20
>> implicit conversion:
>>
>> std::shared_ptr<const Master> Slave::getMaster() const
>> {
>> return m_master; // ERROR: No implicit conversion from=20
>> propagate_const<shared_ptr>!
>> }
>>
>> Using get() here would be the perfect way to introduce use after free=20
>> bugs:
>>
>> std::shared_ptr<const Master> Slave::getMaster() const
>> {
>> const Master* master =3D m_master.get();
>> return std::shared_ptr<Master>( master ); // INCORRECT: Two owners=20
>> for one object!
>> }
>>
>> And using get_underlying will both break const correctness and fail to=
=20
>> provide me with the correct return type:
>>
>> std::shared_ptr<const Master> Slave::getMaster() const
>> {
>> auto master =3D std::get_underlying( m_master ); // This is an=20
>> std::shared_ptr<Master>
>> return master; // ERROR: No implicit conversion to=20
>> std::shared_ptr<const Master>!
>> }
>>
>> To implement this method, in addition to the const-incorrect=20
>> get_underlying interface, I would also need a const cast!
>>
>> std::shared_ptr<const Master> Slave::getMaster() const
>> {
>> auto master =3D std::get_underlying( m_master );=20
>> return std::const_pointer_cast<const Master>( master );
>> }
>>
>>
>> For sure, I would never want an abomination like this to pass code revie=
w=20
>> :)
>>
>>
>> Agreed.
> =20
>
>> This little thought experiment showcases two major issues with=20
>> get_underlying:
>>
>> - Counter to the goal of of propagate_const, it makes it trivial to=
=20
>> violate const-correctness without the compiler noticing (or, for that=
=20
>> matter, any developer or static analysis tool that is unfamiliar with=
the=20
>> propagate_const API).
>> - By not returning the const-correct type, get_underlying can make it=
=20
>> unnecessarily hard to implement const-correct interfaces.
>> =20
>> The authors of the original propagate_const proposal were aware that the=
=20
>> get_underlying interface was suboptimal, and for this reason they opted =
to=20
>> make it less usable by hiding it as a free function instead of making it=
a=20
>> proper method of propagate_const. But personnally, I would go further: d=
rop=20
>> get_underlying altogether, and replace it with something that is both=20
>> const-correct and respectful of pointer ownership issues.
>>
>>
>> My counter-proposal would be to have a propagate_const method, maybe=20
>> called "share()", which enables sharing access to the data pointed by=20
>> propagate_const with due respect paid to both const-correctness and the=
=20
>> underlying pointer's ownership semantics. In practice:
>>
>> - If called on propagate_const<T*>, share() should return a T*
>> - If called on const propagate_const<T*>, share() should return a=20
>> const T*
>> - If called on propagate_const<shared_ptr<T>>, share() should return=
=20
>> a shared_ptr<T>
>> - If called on const propagate_const<shared_ptr<T>>, share() should=
=20
>> return a shared_ptr<const T>
>> - share() should not be defined on propagate_const<unique_ptr<T>>=20
>> since that pointer does not support sharing ownership
>>
>> In addition, as a convenience, whenever a share() method exists, an=20
>> implicit cast could be provided to the underlying pointer type as a way =
to=20
>> make such ownership sharing more convenient and to enable things like=20
>> comparison of containers of shared_ptr<T> with containers of=20
>> propagate_const<shared_ptr<T>> through standard (STL-ish) algorithms.
>>
>>
>> If you really, positively want something like get_underlying to exist, i=
t=20
>> should only be applicable to non-const object. This alone would make it=
=20
>> const-correct, and thus remove the need to implement it as a free functi=
on=20
>> and be careful when using it.
>>
>>
>> With this simple change, shameless people who want to get their hands=20
>> dirty and extract a mutable pointer from a const wrapper will then need =
to=20
>> do some extra work, as should be expected of them in my opinion:
>>
>>
>> using ConstPropagator =3D std::propagate_const<std::shared_ptr<T>>;
>>
>> const ConstPropagator& immutableRef =3D ... ;
>> std::shared_ptr<T> mutableRef =3D const_cast<ConstPropagator*>( &immutab=
leRef=20
>> )->get_underlying();
>>
>> --=20
>> You received this message because you are subscribed to the Google Group=
s=20
>> "ISO C++ Standard - Future Proposals" group.
>> To unsubscribe from this group and stop receiving emails from it, send a=
n=20
>> email to std-proposal...@isocpp.org <javascript:>.
>> To post to this group, send email to std-pr...@isocpp.org <javascript:>.
>> To view this discussion on the web visit=20
>> https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/9ad3f22a-a6=
19-4488-bf1e-303641c968b9%40isocpp.org=20
>> <https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/9ad3f22a-a=
619-4488-bf1e-303641c968b9%40isocpp.org?utm_medium=3Demail&utm_source=3Dfoo=
ter>
>> .
>>
>
>
--=20
You received this message because you are subscribed to the Google Groups "=
ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp=
..org/d/msgid/std-proposals/189d5497-36bb-4c9c-8992-13d7eaa36b6b%40isocpp.or=
g.
------=_Part_275_170315023.1487952368946
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr"><br><br>Le mercredi 22 f=C3=A9vrier 2017 09:56:10 UTC+1, J=
onathan Coe a =C3=A9crit=C2=A0:<blockquote class=3D"gmail_quote" style=3D"m=
argin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"=
><div dir=3D"ltr"><div><br><div class=3D"gmail_quote">On 21 February 2017 a=
t 14:54, Hadrien Grasland wrote:<br><blockquote class=3D"gmail_quote" style=
=3D"margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(20=
4,204,204);border-left-style:solid;padding-left:1ex"><div dir=3D"ltr">Hi ev=
eryone,<br><br>I am not sure what this group's policy is regarding resu=
rrection of old threads, so I have decided to create a new one. But I would=
like to second two comments that were previously made on the propagate_con=
st proposal of N4617:<br><br>1/ That it should be possible to copy a propag=
ate_const<T> wrapper ( <a href=3D"https://groups.google.com/a/isocpp.=
org/forum/#!searchin/std-proposals/propagate_const/std-proposals/1uDKcA9bss=
U/AZ-aQNAZAAAJ" target=3D"_blank" rel=3D"nofollow" onmousedown=3D"this.href=
=3D'https://groups.google.com/a/isocpp.org/forum/#!searchin/std-proposa=
ls/propagate_const/std-proposals/1uDKcA9bssU/AZ-aQNAZAAAJ';return true;=
" onclick=3D"this.href=3D'https://groups.google.com/a/isocpp.org/forum/=
#!searchin/std-proposals/propagate_const/std-proposals/1uDKcA9bssU/AZ-aQNAZ=
AAAJ';return true;">https://groups.google.com/a/<wbr>isocpp.org/forum/#=
!searchin/<wbr>std-proposals/propagate_const/<wbr>std-proposals/1uDKcA9bssU=
/AZ-<wbr>aQNAZAAAJ</a> ).<br>2/ That propagate_const<T> should be con=
vertible to a const-correct form of the underlying pointer type ( <a href=
=3D"https://groups.google.com/a/isocpp.org/forum/#!searchin/std-proposals/p=
ropagate_const/std-proposals/7rSMtvQVASk/niLSKtkcBwAJ" target=3D"_blank" re=
l=3D"nofollow" onmousedown=3D"this.href=3D'https://groups.google.com/a/=
isocpp.org/forum/#!searchin/std-proposals/propagate_const/std-proposals/7rS=
MtvQVASk/niLSKtkcBwAJ';return true;" onclick=3D"this.href=3D'https:=
//groups.google.com/a/isocpp.org/forum/#!searchin/std-proposals/propagate_c=
onst/std-proposals/7rSMtvQVASk/niLSKtkcBwAJ';return true;">https://grou=
ps.google.com/a/<wbr>isocpp.org/forum/#!searchin/<wbr>std-proposals/propaga=
te_const/<wbr>std-proposals/7rSMtvQVASk/<wbr>niLSKtkcBwAJ</a> ).<br><br>Let=
me motivate, through some concrete use cases, why I think that both of the=
se changes are necessary.<br><br><br><font size=3D"4">Need for shallow copy=
operations</font><br><br>There are many reasons to use pointers in C++. A =
very common use case, however, is dynamic polymorphism:<br><ul><li>Differen=
t implementations of a virtual interface may have different sizes</li><li>C=
++, in its commendable quest for zero-cost abstraction, does not provide na=
tive support for dynamically sized types</li><li>When combined, these two l=
anguage design choices lead to the conclusion that if the set of implementa=
tions of an interface is not known as compile time, polymorphism intrinsica=
lly requires pointer indirection in C++</li></ul>For this use case, pointer=
constness semantics are inadequate. If you use a pointer-to-const, you can=
not modify the target object, which is often fine but tends to be excessive=
ly limitating for some use cases, particularly when implementing constructo=
rs and factory functions. If, on the other hand, you use a pointer-to-mutab=
le, you open a const-correctness hole by allowing yourself to mutate a cons=
t object without any compiler warning. Quite frankly, none of these options=
are very appealing.<br><br><div style=3D"background-color:rgb(250,250,250)=
;border:1px solid rgb(187,187,187)"><code><div><span style=3D"color:rgb(136=
,0,0)">// Pointer-to-const restricts some valid use cases of polymorphism</=
span><span style=3D"color:rgb(0,0,0)"><br></span><span style=3D"color:rgb(0=
,0,136)">class</span><span style=3D"color:rgb(0,0,0)"> </span><span style=
=3D"color:rgb(102,0,102)">FirstTry</span><span style=3D"color:rgb(0,0,0)"><=
br></span><span style=3D"color:rgb(102,102,0)">{</span><span style=3D"color=
:rgb(0,0,0)"></span><br><code><span style=3D"color:rgb(0,0,136)">private</s=
pan><span style=3D"color:rgb(102,102,0)">:</span><span style=3D"color:rgb(0=
,0,0)"><br>=C2=A0 std</span><span style=3D"color:rgb(102,102,0)">::</span><=
span style=3D"color:rgb(0,0,0)">unique_ptr</span><span style=3D"color:rgb(1=
02,102,0)"><</span><span style=3D"color:rgb(0,0,136)">const</span><span =
style=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,0,102)">Int=
erface</span><span style=3D"color:rgb(102,102,0)">></span><span style=3D=
"color:rgb(0,0,0)"> m_ptr</span><span style=3D"color:rgb(102,102,0)">;</spa=
n><span style=3D"color:rgb(0,0,0)"><br><br></span></code><span style=3D"col=
or:rgb(0,0,136)">public</span><span style=3D"color:rgb(102,102,0)">:</span>=
<span style=3D"color:rgb(0,0,0)"><br>=C2=A0 </span><span style=3D"color:rgb=
(102,0,102)">FirstTry</span><span style=3D"color:rgb(102,102,0)">()</span><=
span style=3D"color:rgb(0,0,0)"><br>=C2=A0 =C2=A0 </span><span style=3D"col=
or:rgb(102,102,0)">:</span><span style=3D"color:rgb(0,0,0)"> m_ptr</span><s=
pan style=3D"color:rgb(102,102,0)">{</span><span style=3D"color:rgb(0,0,0)"=
> std</span><span style=3D"color:rgb(102,102,0)">::</span><span style=3D"co=
lor:rgb(0,0,0)">make_unique</span><span style=3D"color:rgb(102,102,0)"><=
</span><span style=3D"color:rgb(102,0,102)">Implementatio<wbr>n</span><span=
style=3D"color:rgb(102,102,0)">>(</span><span style=3D"color:rgb(0,0,0)=
"> </span><span style=3D"color:rgb(102,102,0)">...</span><span style=3D"col=
or:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,102,0)">)</span><span s=
tyle=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,102,0)">}</s=
pan><span style=3D"color:rgb(0,0,0)"><br>=C2=A0 </span><span style=3D"color=
:rgb(102,102,0)">{</span><span style=3D"color:rgb(0,0,0)"><br>=C2=A0 =C2=A0=
m_ptr</span><span style=3D"color:rgb(102,102,0)">-></span><span style=
=3D"color:rgb(0,0,0)">setParent</span><span style=3D"color:rgb(102,102,0)">=
(</span><span style=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(1=
02,102,0)">*</span><span style=3D"color:rgb(0,0,136)">this</span><span styl=
e=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,102,0)">);</spa=
n><span style=3D"color:rgb(0,0,0)"> =C2=A0</span><span style=3D"color:rgb(1=
36,0,0)">// ERROR:</span><span style=3D"color:rgb(0,0,0)"> Can't mutate=
through a const pointer!<br>=C2=A0 </span><span style=3D"color:rgb(102,102=
,0)">}</span><span style=3D"color:rgb(0,0,0)"><br></span><span style=3D"col=
or:rgb(102,102,0)">};</span><span style=3D"color:rgb(0,0,0)"><br><br><br></=
span><span style=3D"color:rgb(136,0,0)">// Pointer-to-mutable breaks const =
correctness of polymorphism</span><span style=3D"color:rgb(0,0,0)"><br></sp=
an><span style=3D"color:rgb(0,0,136)">class</span><span style=3D"color:rgb(=
0,0,0)"> </span><span style=3D"color:rgb(102,0,102)">SecondTry</span><span =
style=3D"color:rgb(0,0,0)"><br></span><span style=3D"color:rgb(102,102,0)">=
{</span><span style=3D"color:rgb(0,0,0)"><br>private:<br>=C2=A0 std::unique=
_ptr<Interface> m_ptr;<br><br></span><span style=3D"color:rgb(0,0,136=
)">public</span><span style=3D"color:rgb(102,102,0)">:</span><span style=3D=
"color:rgb(0,0,0)"><br>=C2=A0 </span><span style=3D"color:rgb(102,0,102)">S=
econdTry</span><span style=3D"color:rgb(102,102,0)">()</span><span style=3D=
"color:rgb(0,0,0)"><br>=C2=A0 =C2=A0 </span><span style=3D"color:rgb(102,10=
2,0)">:</span><span style=3D"color:rgb(0,0,0)"> m_ptr</span><span style=3D"=
color:rgb(102,102,0)">{</span><span style=3D"color:rgb(0,0,0)"> std</span><=
span style=3D"color:rgb(102,102,0)">::</span><span style=3D"color:rgb(0,0,0=
)">make_unique</span><span style=3D"color:rgb(102,102,0)"><</span><span =
style=3D"color:rgb(102,0,102)">Implementatio<wbr>n</span><span style=3D"col=
or:rgb(102,102,0)">>(</span><span style=3D"color:rgb(0,0,0)"> </span><sp=
an style=3D"color:rgb(102,102,0)">...</span><span style=3D"color:rgb(0,0,0)=
"> </span><span style=3D"color:rgb(102,102,0)">)</span><span style=3D"color=
:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,102,0)">}</span><span sty=
le=3D"color:rgb(0,0,0)"><br>=C2=A0 </span><span style=3D"color:rgb(102,102,=
0)">{</span><span style=3D"color:rgb(0,0,0)"><br>=C2=A0 =C2=A0 m_ptr</span>=
<span style=3D"color:rgb(102,102,0)">-></span><span style=3D"color:rgb(0=
,0,0)">setParent</span><span style=3D"color:rgb(102,102,0)">(</span><span s=
tyle=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,102,0)">*</s=
pan><span style=3D"color:rgb(0,0,136)">this</span><span style=3D"color:rgb(=
0,0,0)"> </span><span style=3D"color:rgb(102,102,0)">);</span><span style=
=3D"color:rgb(0,0,0)"> =C2=A0</span><span style=3D"color:rgb(136,0,0)">// T=
his is now okay...</span><span style=3D"color:rgb(0,0,0)"><br>=C2=A0 </span=
><span style=3D"color:rgb(102,102,0)">}</span><span style=3D"color:rgb(0,0,=
0)"><br><br>=C2=A0 </span><span style=3D"color:rgb(0,0,136)">int</span><spa=
n style=3D"color:rgb(0,0,0)"> someRandomAccessor</span><span style=3D"color=
:rgb(102,102,0)">()</span><span style=3D"color:rgb(0,0,0)"> </span><span st=
yle=3D"color:rgb(0,0,136)">const</span><span style=3D"color:rgb(0,0,0)"> </=
span><span style=3D"color:rgb(102,102,0)">{</span><span style=3D"color:rgb(=
0,0,0)"><br>=C2=A0 =C2=A0 m_ptr</span><span style=3D"color:rgb(102,102,0)">=
-></span><span style=3D"color:rgb(0,0,0)">setSomething</span><span style=
=3D"color:rgb(102,102,0)">();</span><span style=3D"color:rgb(0,0,0)"> =C2=
=A0</span><span style=3D"color:rgb(136,0,0)">// ...but unfortunately, this =
is okay too :-(</span><span style=3D"color:rgb(0,0,0)"><br>=C2=A0 =C2=A0 </=
span><span style=3D"color:rgb(0,0,136)">return</span><span style=3D"color:r=
gb(0,0,0)"> </span><span style=3D"color:rgb(0,102,102)">42</span><span styl=
e=3D"color:rgb(102,102,0)">;</span><span style=3D"color:rgb(0,0,0)"><br>=C2=
=A0 </span><span style=3D"color:rgb(102,102,0)">}</span><span style=3D"colo=
r:rgb(0,0,0)"><br>};</span><span style=3D"color:rgb(0,0,0)"><br></span></di=
v></code></div><br>Now, if we are on the same wavelength, and I will assume=
in the following that this is the case, this is precisely the kind of issu=
e that propagate_const is designed to help with.<br><br>Let us now turn our=
attention to copy operations. Ideally, polymorphic objects should be deep-=
copyable, just like any regular value type. Unfortunately, in another appli=
cation of the zero cost abstraction principle, C++ provides no easy way to =
do this. Anyone who wants to combine value semantics and dynamic polymorphi=
sm needs to provide explicit support for deep copies across the entire clas=
s hierarchy, which is cumbersome to begin with and can get problematic when=
interacting with third-party libraries.<br></div></blockquote><div><br></d=
iv><div>Arthur has addressed your concerns with the same points I would hav=
e raised (thanks Arthur). On the related topic of deep-copies and const-pro=
pagation we're working on polymorphic_value which aims to tackle copies=
through class heirarchies. I'd be interested if you have a use case wh=
ere one of propagate_const and polymorphic_value does not solve your proble=
m.=C2=A0</div><div><br></div><div><a href=3D"https://github.com/jbcoe/polym=
orphic_value/blob/master/talks/2017_1_25_cxx_london.md" target=3D"_blank" r=
el=3D"nofollow" onmousedown=3D"this.href=3D'https://www.google.com/url?=
q\x3dhttps%3A%2F%2Fgithub.com%2Fjbcoe%2Fpolymorphic_value%2Fblob%2Fmaster%2=
Ftalks%2F2017_1_25_cxx_london.md\x26sa\x3dD\x26sntz\x3d1\x26usg\x3dAFQjCNHi=
VpbjR-rhERETegsvD58f1-r4Hw';return true;" onclick=3D"this.href=3D'h=
ttps://www.google.com/url?q\x3dhttps%3A%2F%2Fgithub.com%2Fjbcoe%2Fpolymorph=
ic_value%2Fblob%2Fmaster%2Ftalks%2F2017_1_25_cxx_london.md\x26sa\x3dD\x26sn=
tz\x3d1\x26usg\x3dAFQjCNHiVpbjR-rhERETegsvD58f1-r4Hw';return true;">htt=
ps://github.com/jbcoe/<wbr>polymorphic_value/blob/master/<wbr>talks/2017_1_=
25_cxx_london.md</a><br></div><div><a href=3D"https://github.com/jbcoe/poly=
morphic_value/blob/master/draft.md" target=3D"_blank" rel=3D"nofollow" onmo=
usedown=3D"this.href=3D'https://www.google.com/url?q\x3dhttps%3A%2F%2Fg=
ithub.com%2Fjbcoe%2Fpolymorphic_value%2Fblob%2Fmaster%2Fdraft.md\x26sa\x3dD=
\x26sntz\x3d1\x26usg\x3dAFQjCNE-iatIgiyMpvR4yv-KdAxgAdZuhQ';return true=
;" onclick=3D"this.href=3D'https://www.google.com/url?q\x3dhttps%3A%2F%=
2Fgithub.com%2Fjbcoe%2Fpolymorphic_value%2Fblob%2Fmaster%2Fdraft.md\x26sa\x=
3dD\x26sntz\x3d1\x26usg\x3dAFQjCNE-iatIgiyMpvR4yv-KdAxgAdZuhQ';return t=
rue;">https://github.com/jbcoe/<wbr>polymorphic_value/blob/master/<wbr>draf=
t.md</a></div></div></div></div></blockquote><div><br>I love the idea of po=
lymorphic_value. I think that once available, it should be taught to new C+=
+ programmers as the standard way to manipulate polymorphic objects, and th=
at anyone should strongly consider using it for this purpose in new codebas=
es, or whenever a mature codebase gets completely rewritten.<br><br>One thi=
ng which I do not think polymorphic_value can do, however, is be gradually =
introduced in an existing codebase. By design, polymorphic_value has to be =
constructed from pointer-to-derived, and is thus fundamentally incompatible=
with the massive body of existing C++ code that uses pointer-to-base for p=
olymorphism. Once you introduce a polymorphic_value somewhere, you also nee=
d to rewrite every other piece of client code that uses the same class hier=
archy, often including third-party libraries that you may have no easy acce=
ss to. Even though the final code will be cleaner, the process will be even=
more costly as adding clone() methods everywhere. And for large projects, =
that will just not be seen as a viable endeavour.<br><br>I believe that wit=
h the modifications that I am proposing, propagate_const would be a better =
fit for such refactoring purposes. With it, one could progressively introdu=
ce const-correct pointer manipulation at relatively little cost. Sure, you =
wouldn't have deep copies, but hey, developers of existing codebases ne=
ver had them to begin with. So we aren't going to miss them in a contex=
t of progressive code evolution. And if error-prone shallow copies are hard=
er to perform by accident, well, all the better.<br><br>=C2=A0<br><br></div=
><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;bo=
rder-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div><div cl=
ass=3D"gmail_quote"><blockquote class=3D"gmail_quote" style=3D"margin:0px 0=
px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);borde=
r-left-style:solid;padding-left:1ex"><div dir=3D"ltr"><br><div style=3D"bac=
kground-color:rgb(250,250,250);border:1px solid rgb(187,187,187)"><code><di=
v><span style=3D"color:rgb(0,0,136)">class</span><span style=3D"color:rgb(0=
,0,0)"> </span><span style=3D"color:rgb(102,0,102)">Interface</span><span s=
tyle=3D"color:rgb(0,0,0)"><br></span><span style=3D"color:rgb(102,102,0)">{=
</span><span style=3D"color:rgb(0,0,0)"><br></span><span style=3D"color:rgb=
(0,0,136)">public</span><span style=3D"color:rgb(102,102,0)">:</span><span =
style=3D"color:rgb(0,0,0)"><br>=C2=A0 </span><span style=3D"color:rgb(136,0=
,0)">// This kind of boilerplate must be added to every class supporting de=
ep copy...</span><span style=3D"color:rgb(0,0,0)"><br>=C2=A0 </span><span s=
tyle=3D"color:rgb(0,0,136)">virtual</span><span style=3D"color:rgb(0,0,0)">=
std</span><span style=3D"color:rgb(102,102,0)">::</span><span style=3D"col=
or:rgb(0,0,0)">unique_ptr</span><span style=3D"color:rgb(102,102,0)"><</=
span><span style=3D"color:rgb(102,0,102)">Interface</span><span style=3D"co=
lor:rgb(102,102,0)">></span><span style=3D"color:rgb(0,0,0)"> clone</spa=
n><span style=3D"color:rgb(102,102,0)">()</span><span style=3D"color:rgb(0,=
0,0)"> </span><span style=3D"color:rgb(0,0,136)">const</span><span style=3D=
"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,102,0)">=3D</span><=
span style=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(0,102,102)=
">0</span><span style=3D"color:rgb(102,102,0)">;</span><span style=3D"color=
:rgb(0,0,0)"><br></span><span style=3D"color:rgb(102,102,0)">};</span><span=
style=3D"color:rgb(0,0,0)"><br><br></span><span style=3D"color:rgb(0,0,136=
)">class</span><span style=3D"color:rgb(0,0,0)"> </span><span style=3D"colo=
r:rgb(102,0,102)">Implementation</span><span style=3D"color:rgb(0,0,0)"> </=
span><span style=3D"color:rgb(102,102,0)">:</span><span style=3D"color:rgb(=
0,0,0)"> </span><span style=3D"color:rgb(0,0,136)">public</span><span style=
=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,0,102)">Interfac=
e</span><span style=3D"color:rgb(0,0,0)"><br></span><span style=3D"color:rg=
b(102,102,0)">{</span><span style=3D"color:rgb(0,0,0)"><br></span><span sty=
le=3D"color:rgb(0,0,136)">public</span><span style=3D"color:rgb(102,102,0)"=
>:</span><span style=3D"color:rgb(0,0,0)"><br>=C2=A0 </span><span style=3D"=
color:rgb(136,0,0)">// ...and replicated again and again in every single no=
n-abstract child. No fun.</span><span style=3D"color:rgb(0,0,0)"><br>=C2=A0=
std</span><span style=3D"color:rgb(102,102,0)">::</span><span style=3D"col=
or:rgb(0,0,0)">unique_ptr</span><span style=3D"color:rgb(102,102,0)"><</=
span><span style=3D"color:rgb(102,0,102)">Interface</span><span style=3D"co=
lor:rgb(102,102,0)">></span><span style=3D"color:rgb(0,0,0)"> clone</spa=
n><span style=3D"color:rgb(102,102,0)">()</span><span style=3D"color:rgb(0,=
0,0)"> </span><span style=3D"color:rgb(0,0,136)">const</span><span style=3D=
"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(0,0,136)">final</span><=
span style=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(0,0,136)">=
override</span><span style=3D"color:rgb(0,0,0)"><br>=C2=A0 </span><span sty=
le=3D"color:rgb(102,102,0)">{</span><span style=3D"color:rgb(0,0,0)"><br>=
=C2=A0 =C2=A0 </span><span style=3D"color:rgb(0,0,136)">return</span><span =
style=3D"color:rgb(0,0,0)"> std</span><span style=3D"color:rgb(102,102,0)">=
::</span><span style=3D"color:rgb(0,0,0)">make_unique</span><span style=3D"=
color:rgb(102,102,0)"><</span><span style=3D"color:rgb(102,0,102)">Imple=
mentatio<wbr>n</span><span style=3D"color:rgb(102,102,0)">>(</span><span=
style=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,102,0)">*<=
/span><span style=3D"color:rgb(0,0,136)">this</span><span style=3D"color:rg=
b(0,0,0)"> </span><span style=3D"color:rgb(102,102,0)">);</span><span style=
=3D"color:rgb(0,0,0)"><br>=C2=A0 </span><span style=3D"color:rgb(102,102,0)=
">}</span><span style=3D"color:rgb(0,0,0)"><br></span><span style=3D"color:=
rgb(102,102,0)">};</span></div></code></div><br>But thankfully, sometimes, =
we can do without a true deep copy, and satisfy ourselves with a shallow co=
py. And in fact, for some use cases, a shallow copy will even be exactly wh=
at we want:<br><ul><li>When building some variety of search acceleration st=
ructure (hashmap, neighbour locator, axis-aligned-bounding box...)</li><li>=
When many "slave objects" share a reference to some common, poten=
tially large "master object"</li></ul>Unfortunately, even though =
all standard C++ pointer types make it trivial to shallow-copy a polymorphi=
c object, propagate_const gets in our way here by being move only. I see th=
is design choice as a shortcoming, because it reduces the usefulness of pro=
pagate_const in many dynamic polymorphism use cases where the underlying po=
inter type would have done just fine. For example, without shallow copy ope=
rations...<br><ul><li>You cannot let the compiler implement a shallow copy =
constructor for you using "=3D default"<br></li><li>You cannot us=
e std::copy_if to find objects matching some predicate in an internal datas=
et</li><li>You cannot easily build object search acceleration structures, s=
uch as hashmaps or AABBs</li><li>You cannot easily share a reference to an =
object wrapped by propagate_const with other objects</li></ul><p>I am aware=
that shallow copies are possible using the get() and get_underlying() oper=
ations. I do not see this as a satisfactory answer, because it breaks every=
STL-ish algorithm that expects a copy constructor. Of course, I could buil=
d a propagate_const wrapper that has a copy constructor myself, and this is=
what I would end up doing if propagate_const were accepted in the STL in i=
ts current form. But I think that the use cases that I mentioned above are =
valid enough to warrant changing the design of propagate_const instead.</p>=
</div></blockquote><blockquote class=3D"gmail_quote" style=3D"margin:0px 0p=
x 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border=
-left-style:solid;padding-left:1ex"><div dir=3D"ltr"><p></p><p>I am aware t=
hat in order to be const-correct, a shallow copy constructor for propagate_=
const would need to operate from a non-const reference. I am fine with this=
tradeoff. The main reason we usually allow ourselves to make mutable copie=
s from const objects is because we assume the copy to be independent from t=
he original object. This assumption is broken when making shallow copies th=
rough pointer or reference types. Sure, many APIs will be broken initially,=
but that is unavoidable when fixing old programming language flaws. Bugs w=
ill be reported, and interfaces will be fixed, the way it's always been=
done.<br></p><p><br></p><p>For prior art, if you look at the STL's doc=
umentation, you will find that customizable algorithms that copy const data=
, such as std::copy_if, explicitly do NOT require the user-provided functio=
ns to consume the data by const-reference. So the idea of copying from non-=
const is not new. The C++ community only needs to re-discover it.<br></p><p=
><br></p></div></blockquote><div><br></div><div>We considered making copies=
from non-const propagate_const legal but were strongly dissuaded by 3 majo=
r standard library implementers.</div></div></div></div></blockquote><div><=
br>Sure, they disagreed. But what were their argument for it? Was it just a=
case of "the maffia said no"?<br><br>=C2=A0</div><blockquote cla=
ss=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #=
ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div><div class=3D"gmail_quo=
te"><div>=C2=A0=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"margi=
n:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204=
);border-left-style:solid;padding-left:1ex"><div dir=3D"ltr"><p></p><p>I am=
also aware of the objection, made in a thread linked above, that "We =
were not confident that a non-const copy constructor would protect=20
the user from accidental loss of const and accidental mutable shared=20
state". As far as I'm concerned, this hand-waving statement was no=
t properly justified. From my perspective, the proposed alternative of usin=
g get_underlying seems to be much bigger breach of const-correctness than a=
copy constructor that operates from mutable, because from a semantic point=
of view, get_underlying is essentially a silent const_cast that standard c=
ode analysis tools won't catch.</p><p><br></p></div></blockquote><div><=
br></div><div>We added `get_underlying` as an nicer alternative to `reinter=
pret_cast`, which would give access to the underlying pointer.<br></div><di=
v><br></div><div>Our design sought to avoid silent surprise. Granted a user=
can do the wrong thing with get_underlying but the code is written by the =
user, not by the compiler. Forcing the user to be explicit offers some prot=
ection.</div></div></div></div></blockquote><div><br>Been there, done that.=
When you see all the tools that C++ and its standard library provide to he=
lp you shoot yourself in the foot, it is always very tempting to roll your =
own, thinking that surely it will do the job better. Except usually, it doe=
sn't. When what you want is a cast, make it a cast, don't try to ma=
ke it look nicer by burying it in custom abstractions.<br><br>Let's tak=
e a step back and think about this from an API design point of view. Why ma=
kes get_underlying such an poor interface?<br><br>1/ By returning a referen=
ce to the internal pointer object, you leak implementation details<br>2/ By=
leaking implementation details, you lose the ability to maintain the singl=
e most important class invariant of propagate_const, which is that type-saf=
e accesses to a const propagate_const wrapper should not give one write acc=
ess to the internal object.<br><br>Now, I am aware that we are discussing C=
++ API design here. Leaking implementation details and violating class inva=
riants is okay, as long as it can give me that 0.1% performance improvement=
in some very obscure use case that someone thought about someday. One a si=
ngle compiler. With the right combination of flags. Until the next release.=
Perhaps.<br><br>But in the case of get_underlying, you could actually leak=
these implementation details, if you really wanted to, without violating y=
our class invariants along the way. The fix is simple, really. Make get_und=
erlying a non-const method of the propagate_const object, and drop get_unde=
rlying from const. Be serious about interface guarantees.<br><br>If your us=
ers come whining because they can't easily extract a pointer-to-mutable=
from a const object, try to give them the talk about const correctness. If=
they won't listen, tell them to const_cast the propagate_const wrapper=
.. But don't compromise your API just to make stupid operations more con=
venient to write. It is never worth it.<br><br><br>=C2=A0<br></div><blockqu=
ote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left=
: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div><div class=3D"gm=
ail_quote"><blockquote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.=
8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-st=
yle:solid;padding-left:1ex"><div dir=3D"ltr"><p></p><br><font size=3D"4">Co=
nst-correct convertibility to the underlying pointer type</font><br><br>Som=
etimes, one has to extract a pointer back from the propagate_const wrapper.=
I would see two main use cases for this:<br><ul><li>To feed a legacy API t=
hat is not (yet?) compatible with propagate_const.<br></li><li>To turn a pr=
opagate_const<T> into a pointer-to-const, when the recipient should n=
ot be able to modify to the target object</li></ul><p>The current propagate=
_const interface provides three ways to do this:</p><ul><li>Call get() and =
get a raw pointer</li><li>Rely on an implicit cast to raw pointer, if avail=
able</li><li>Use get_underlying to access the internals of propagate_const.=
</li></ul><p>As I'm going to elaborate, the first two operations are no=
t always the right tool for the job at hand, because they fail to convey im=
portant ownership information from the underlying pointer type. Whereas get=
_underlying, as currently implemented, is a flawed interface that goes dire=
ctly against the design goals of propagate_const and should be eradicated b=
efore standardization.</p><p><br></p><p>As a use case, suppose that as disc=
ussed previously, I am working a "slave object", which holds a sh=
ared_ptr to an associated "master object" that it shares with oth=
er slaves. To improve the const-correctness of this design, I have decided =
to refactor the shared_ptr into a propagate_const<shared_ptr>. So far=
, so good.<br></p><p><br></p><p></p><div style=3D"background-color:rgb(250,=
250,250);border:1px solid rgb(187,187,187)"><code><div><span style=3D"color=
:rgb(0,0,136)">class</span><span style=3D"color:rgb(0,0,0)"> </span><span s=
tyle=3D"color:rgb(102,0,102)">Master</span><span style=3D"color:rgb(0,0,0)"=
><br></span><span style=3D"color:rgb(102,102,0)">{</span><span style=3D"col=
or:rgb(0,0,0)"><br>=C2=A0 </span><span style=3D"color:rgb(102,102,0)">...</=
span><span style=3D"color:rgb(0,0,0)"><br></span><span style=3D"color:rgb(1=
02,102,0)">};</span><span style=3D"color:rgb(0,0,0)"><br><br></span><span s=
tyle=3D"color:rgb(0,0,136)">class</span><span style=3D"color:rgb(0,0,0)"> <=
/span><span style=3D"color:rgb(102,0,102)">Slave</span><span style=3D"color=
:rgb(0,0,0)"><br></span><span style=3D"color:rgb(102,102,0)">{</span><span =
style=3D"color:rgb(0,0,0)"><br></span><span style=3D"color:rgb(0,0,136)">pr=
ivate</span><span style=3D"color:rgb(102,102,0)">:</span><span style=3D"col=
or:rgb(0,0,0)"><br>=C2=A0 </span><span style=3D"color:rgb(136,0,0)">// This=
was refactored from "std::shared_ptr<Master> m_master"</sp=
an><span style=3D"color:rgb(0,0,0)"><br>=C2=A0 std</span><span style=3D"col=
or:rgb(102,102,0)">::</span><span style=3D"color:rgb(0,0,0)">propagate_cons=
t</span><span style=3D"color:rgb(102,102,0)"><</span><span style=3D"colo=
r:rgb(0,0,0)">std</span><span style=3D"color:rgb(102,102,0)">::</span><span=
style=3D"color:rgb(0,0,0)">shar<wbr>ed_ptr</span><span style=3D"color:rgb(=
102,102,0)"><</span><span style=3D"color:rgb(102,0,102)">Master</span><s=
pan style=3D"color:rgb(102,102,0)">>></span><span style=3D"color:rgb(=
0,0,0)"> m_master</span><span style=3D"color:rgb(102,102,0)">;</span><span =
style=3D"color:rgb(0,0,0)"><br><br></span><span style=3D"color:rgb(0,0,136)=
">public</span><span style=3D"color:rgb(102,102,0)">:</span><span style=3D"=
color:rgb(0,0,0)"><br>=C2=A0 </span><span style=3D"color:rgb(102,0,102)">Sl=
ave</span><span style=3D"color:rgb(102,102,0)">(</span><span style=3D"color=
:rgb(0,0,0)"> std</span><span style=3D"color:rgb(102,102,0)">::</span><span=
style=3D"color:rgb(0,0,0)">shared_ptr</span><span style=3D"color:rgb(102,1=
02,0)"><</span><span style=3D"color:rgb(102,0,102)">Master</span><span s=
tyle=3D"color:rgb(102,102,0)">></span><span style=3D"color:rgb(0,0,0)"> =
master </span><span style=3D"color:rgb(102,102,0)">)</span><span style=3D"c=
olor:rgb(0,0,0)"><br>=C2=A0 =C2=A0 </span><span style=3D"color:rgb(102,102,=
0)">:</span><span style=3D"color:rgb(0,0,0)"> m_master</span><span style=3D=
"color:rgb(102,102,0)">(</span><span style=3D"color:rgb(0,0,0)">master</spa=
n><span style=3D"color:rgb(102,102,0)">)</span><span style=3D"color:rgb(0,0=
,0)"><br>=C2=A0 </span><span style=3D"color:rgb(102,102,0)">{</span><span s=
tyle=3D"color:rgb(0,0,0)"><br>=C2=A0 =C2=A0 m_master</span><span style=3D"c=
olor:rgb(102,102,0)">-></span><span style=3D"color:rgb(0,0,0)">addSlave<=
/span><span style=3D"color:rgb(102,102,0)">(</span><span style=3D"color:rgb=
(0,0,0)"> </span><span style=3D"color:rgb(102,102,0)">*</span><span style=
=3D"color:rgb(0,0,136)">this</span><span style=3D"color:rgb(0,0,0)"> </span=
><span style=3D"color:rgb(102,102,0)">);</span><span style=3D"color:rgb(0,0=
,0)"><br>=C2=A0 </span><span style=3D"color:rgb(102,102,0)">}</span><span s=
tyle=3D"color:rgb(0,0,0)"><br><br>=C2=A0 ...<br></span><span style=3D"color=
:rgb(102,102,0)">};</span><span style=3D"font-family:arial,sans-serif;backg=
round-color:rgb(255,255,255)">=C2=A0</span></div></code></div></div></block=
quote><blockquote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;b=
order-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:s=
olid;padding-left:1ex"><div dir=3D"ltr"><p></p>But as I proceed with the re=
factoring, I discover that the Slave class used to provide a method that sh=
ares access to its master:<br><br><div style=3D"background-color:rgb(250,25=
0,250);border:1px solid rgb(187,187,187)"><code><div><span style=3D"color:r=
gb(0,0,0)">std</span><span style=3D"color:rgb(102,102,0)">::</span><span st=
yle=3D"color:rgb(0,0,0)">shared_ptr</span><span style=3D"color:rgb(102,102,=
0)"><</span><span style=3D"color:rgb(102,0,102)">Master</span><span styl=
e=3D"color:rgb(102,102,0)">></span><span style=3D"color:rgb(0,0,0)"> </s=
pan><span style=3D"color:rgb(102,0,102)">Slave</span><span style=3D"color:r=
gb(102,102,0)">::</span><span style=3D"color:rgb(0,0,0)">getMaster</span><s=
pan style=3D"color:rgb(102,102,0)">()</span><span style=3D"color:rgb(0,0,0)=
"> </span><span style=3D"color:rgb(0,0,136)">const</span><span style=3D"col=
or:rgb(0,0,0)"><br></span><span style=3D"color:rgb(102,102,0)">{</span><spa=
n style=3D"color:rgb(0,0,0)"><br>=C2=A0 </span><span style=3D"color:rgb(0,0=
,136)">return</span><span style=3D"color:rgb(0,0,0)"> m_master</span><span =
style=3D"color:rgb(102,102,0)">;</span><span style=3D"color:rgb(0,0,0)"><br=
></span><span style=3D"color:rgb(102,102,0)">}</span></div></code></div><br=
>This does not compile anymore. And I'm happy about that: it should nev=
er have compiled to begin with. Returning non-const access to my members fr=
om a const method definitely does not match my idea of const-correctness!<b=
r><br>Instead, I would like to only provide const access to the master obje=
ct, like so:<br><br><div style=3D"background-color:rgb(250,250,250);border:=
1px solid rgb(187,187,187)"><code><div><span style=3D"color:rgb(0,0,0)">std=
</span><span style=3D"color:rgb(102,102,0)">::</span><span style=3D"color:r=
gb(0,0,0)">shared_ptr</span><span style=3D"color:rgb(102,102,0)"><</span=
><span style=3D"color:rgb(0,0,136)">const</span><span style=3D"color:rgb(0,=
0,0)"> </span><span style=3D"color:rgb(102,0,102)">Master</span><span style=
=3D"color:rgb(102,102,0)">></span><span style=3D"color:rgb(0,0,0)"> </sp=
an><span style=3D"color:rgb(102,0,102)">Slave</span><span style=3D"color:rg=
b(102,102,0)">::</span><span style=3D"color:rgb(0,0,0)">getMaster</span><sp=
an style=3D"color:rgb(102,102,0)">()</span><span style=3D"color:rgb(0,0,0)"=
> </span><span style=3D"color:rgb(0,0,136)">const</span><span style=3D"colo=
r:rgb(102,102,0)">;</span></div></code></div><br>If my clients are well-beh=
aved and do not mutate the master object, this will be a minimally invasive=
interface change. It will require only minor client rewrites, the kind tha=
t could be automated by sed or an IDE. It could even require no client rewr=
ite at all if I end up being lucky and have clients that were using auto an=
d friends.<br><br>Unfortunately, the current propagate_const interface does=
not allow me to implement this method in a clean way.<br><br></div></block=
quote><div><br></div><div>Sadly that's intended. We can't guarantee=
that "clients are well-behaved and do not mutate the master object&qu=
ot;.</div></div></div></div></blockquote><div><br>And you do not need to, t=
hat's my job. You give me a well-designed const-correct pointer wrapper=
, and I'll use it to improve the const-correctness of the codebase that=
I'm responsible of. If I find broken client code, I will do my best to=
fix it myself, and send an angry e-mail to the person responsible if I don=
't manage. It's called maintenance.<br><br>But when I'm going t=
hrough the pain of doing this, I would like to do so using quality tools th=
at give me the impression that the effort is worthwhile, and won't be u=
ndermined on the day after by someone trying to do something stupid, going =
through the STL documentation and thinking "oh, I know, I'll just =
use get_underlying...".<br><br>When people start to break the thread-s=
afety of our code by introducing mutation in places where it doesn't be=
long, I want them to explicitly write down the dreaded word "cast"=
; and feel that chill going down their spine, telling them that they're=
doing something wrong. I want them to figure out that the basic design of =
their code is broken, and to understand that they need to fix it before sub=
mitting it upstream. I don't want the only line of defense against soft=
ware chaos to be me telling them at the end, when they finally submit their=
merge request, that I can't accept it into our codebase and they need =
to rewrite half of it.<br><br>Good interfaces save everyone's time by m=
aking bad things look bad. Let's have more of them in C++.<br><br>=C2=
=A0</div><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: =
0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div=
><div class=3D"gmail_quote"><div>=C2=A0</div><blockquote class=3D"gmail_quo=
te" style=3D"margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-col=
or:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div dir=3D"l=
tr">The obvious code snippet would not work due to the lack of an appropria=
te implicit conversion:<br><br><div style=3D"background-color:rgb(250,250,2=
50);border:1px solid rgb(187,187,187)"><code><div><span style=3D"color:rgb(=
0,0,0)">std</span><span style=3D"color:rgb(102,102,0)">::</span><span style=
=3D"color:rgb(0,0,0)">shared_ptr</span><span style=3D"color:rgb(102,102,0)"=
><const </span><span style=3D"color:rgb(102,0,102)">Master</span><span s=
tyle=3D"color:rgb(102,102,0)">></span><span style=3D"color:rgb(0,0,0)"> =
</span><span style=3D"color:rgb(102,0,102)">Slave</span><span style=3D"colo=
r:rgb(102,102,0)">::</span><span style=3D"color:rgb(0,0,0)">getMaster</span=
><span style=3D"color:rgb(102,102,0)">()</span><span style=3D"color:rgb(0,0=
,0)"> </span><span style=3D"color:rgb(0,0,136)">const</span><span style=3D"=
color:rgb(0,0,0)"><br></span><span style=3D"color:rgb(102,102,0)">{</span><=
span style=3D"color:rgb(0,0,0)"><br>=C2=A0 </span><span style=3D"color:rgb(=
0,0,136)">return</span><span style=3D"color:rgb(0,0,0)"> m_master</span><sp=
an style=3D"color:rgb(102,102,0)">;</span><span style=3D"color:rgb(0,0,0)">=
=C2=A0 // ERROR: No implicit conversion from propagate_const<shared_ptr&=
gt;!<br></span><span style=3D"color:rgb(102,102,0)">}</span></div></code></=
div><br>Using get() here would be the perfect way to introduce use after fr=
ee bugs:<br><br><div style=3D"background-color:rgb(250,250,250);border:1px =
solid rgb(187,187,187)"><code><div><span style=3D"color:rgb(0,0,0)">std</sp=
an><span style=3D"color:rgb(102,102,0)">::</span><span style=3D"color:rgb(0=
,0,0)">shared_ptr</span><span style=3D"color:rgb(102,102,0)"><const </sp=
an><span style=3D"color:rgb(102,0,102)">Master</span><span style=3D"color:r=
gb(102,102,0)">></span><span style=3D"color:rgb(0,0,0)"> </span><span st=
yle=3D"color:rgb(102,0,102)">Slave</span><span style=3D"color:rgb(102,102,0=
)">::</span><span style=3D"color:rgb(0,0,0)">getMaster</span><span style=3D=
"color:rgb(102,102,0)">()</span><span style=3D"color:rgb(0,0,0)"> </span><s=
pan style=3D"color:rgb(0,0,136)">const</span><span style=3D"color:rgb(0,0,0=
)"><br></span><span style=3D"color:rgb(102,102,0)">{</span><span style=3D"c=
olor:rgb(0,0,0)"><br>=C2=A0 const Master* master =3D m_master.get();<br>=C2=
=A0 </span><span style=3D"color:rgb(0,0,136)">return</span><span style=3D"c=
olor:rgb(0,0,0)"> std::shared_ptr<Master>( master</span><span style=
=3D"color:rgb(102,102,0)"> );</span><span style=3D"color:rgb(0,0,0)">=C2=A0=
// INCORRECT: Two owners for one object!<br></span><span style=3D"color:rg=
b(102,102,0)">}</span></div></code></div><br>And using get_underlying will =
both break const correctness and fail to provide me with the correct return=
type:<br><br><div style=3D"background-color:rgb(250,250,250);border:1px so=
lid rgb(187,187,187)"><code><div><span style=3D"color:rgb(0,0,0)">std</span=
><span style=3D"color:rgb(102,102,0)">::</span><span style=3D"color:rgb(0,0=
,0)">shared_ptr</span><span style=3D"color:rgb(102,102,0)"><const </span=
><span style=3D"color:rgb(102,0,102)">Master</span><span style=3D"color:rgb=
(102,102,0)">></span><span style=3D"color:rgb(0,0,0)"> </span><span styl=
e=3D"color:rgb(102,0,102)">Slave</span><span style=3D"color:rgb(102,102,0)"=
>::</span><span style=3D"color:rgb(0,0,0)">getMaster</span><span style=3D"c=
olor:rgb(102,102,0)">()</span><span style=3D"color:rgb(0,0,0)"> </span><spa=
n style=3D"color:rgb(0,0,136)">const</span><span style=3D"color:rgb(0,0,0)"=
><br></span><span style=3D"color:rgb(102,102,0)">{</span><span style=3D"col=
or:rgb(0,0,0)"><br>=C2=A0 auto master =3D std::get_underlying( m_master );=
=C2=A0 // This is an std::shared_ptr<Master><br>=C2=A0 </span><span s=
tyle=3D"color:rgb(0,0,136)">return</span><span style=3D"color:rgb(0,0,0)"> =
master;=C2=A0 // ERROR: No implicit conversion to std::shared_ptr<const =
Master>!</span><span style=3D"color:rgb(0,0,0)"><br></span><span style=
=3D"color:rgb(102,102,0)">}</span></div></code></div><br>To implement this =
method, in addition to the const-incorrect get_underlying interface, I woul=
d also need a const cast!<br><br><div style=3D"background-color:rgb(250,250=
,250);border:1px solid rgb(187,187,187)"><code><div><span style=3D"color:rg=
b(0,0,0)">std</span><span style=3D"color:rgb(102,102,0)">::</span><span sty=
le=3D"color:rgb(0,0,0)">shared_ptr</span><span style=3D"color:rgb(102,102,0=
)"><const </span><span style=3D"color:rgb(102,0,102)">Master</span><span=
style=3D"color:rgb(102,102,0)">></span><span style=3D"color:rgb(0,0,0)"=
> </span><span style=3D"color:rgb(102,0,102)">Slave</span><span style=3D"co=
lor:rgb(102,102,0)">::</span><span style=3D"color:rgb(0,0,0)">getMaster</sp=
an><span style=3D"color:rgb(102,102,0)">()</span><span style=3D"color:rgb(0=
,0,0)"> </span><span style=3D"color:rgb(0,0,136)">const</span><span style=
=3D"color:rgb(0,0,0)"><br></span><span style=3D"color:rgb(102,102,0)">{</sp=
an><span style=3D"color:rgb(0,0,0)"><br>=C2=A0 auto master =3D std::get_und=
erlying( m_master ); <br>=C2=A0 </span><span style=3D"color:rgb(0,0,136)">r=
eturn</span><span style=3D"color:rgb(0,0,0)"> std::const_pointer_cast<co=
nst Master>( master );</span><span style=3D"color:rgb(0,0,0)"><br></span=
><span style=3D"color:rgb(102,102,0)">}</span></div></code></div><p><br></p=
><p>For sure, I would never want an abomination like this to pass code revi=
ew :)</p><p><br></p></div></blockquote><div>Agreed.</div><div>=C2=A0</div><=
blockquote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-l=
eft-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;pa=
dding-left:1ex"><div dir=3D"ltr"><p></p><p>This little thought experiment s=
howcases two major issues with get_underlying:</p><ul><li>Counter to the go=
al of of propagate_const, it makes it trivial to violate const-correctness =
without the compiler noticing (or, for that matter, any developer or static=
analysis tool that is unfamiliar with the propagate_const API).<br></li><l=
i>By not returning the const-correct type, get_underlying can make it unnec=
essarily hard to implement const-correct interfaces.<br></li></ul><p></p><p=
>The authors of the original propagate_const proposal were aware that the g=
et_underlying interface was suboptimal, and for this reason they opted to m=
ake it less usable by hiding it as a free function instead of making it a p=
roper method of propagate_const. But personnally, I would go further: drop =
get_underlying altogether, and replace it with something that is both const=
-correct and respectful of pointer ownership issues.<br></p><p><br></p><p>M=
y counter-proposal would be to have a propagate_const method, maybe called =
"share()", which enables sharing access to the data pointed by pr=
opagate_const with due respect paid to both const-correctness and the under=
lying pointer's ownership semantics. In practice:</p><ul><li>If called =
on propagate_const<T*>, share() should return a T*</li><li>If called =
on const propagate_const<T*>, share() should return a const T*</li><l=
i>If called on propagate_const<shared_ptr<T>><wbr>, share() sho=
uld return a shared_ptr<T></li><li>If called on const propagate_const=
<shared_ptr<T>><wbr>, share() should return a shared_ptr<con=
st T></li><li>share() should not be defined on propagate_const<unique=
_ptr<T>> since that pointer does not support sharing ownership</li=
></ul><p>In addition, as a convenience, whenever a share() method exists, a=
n implicit cast could be provided to the underlying pointer type as a way t=
o make such ownership sharing more convenient and to enable things like com=
parison of containers of shared_ptr<T> with containers of propagate_c=
onst<shared_ptr<T>> through standard (STL-ish) algorithms.</p><=
p><br></p><p>If you really, positively want something like get_underlying t=
o exist, it should only be applicable to non-const object. This alone would=
make it const-correct, and thus remove the need to implement it as a free =
function and be careful when using it.</p><p><br></p><p>With this simple ch=
ange, shameless people who want to get their hands dirty and extract a muta=
ble pointer from a const wrapper will then need to do some extra work, as s=
hould be expected of them in my opinion:</p><p><br></p><div style=3D"backgr=
ound-color:rgb(250,250,250);border:1px solid rgb(187,187,187)"><code><div><=
span style=3D"color:rgb(0,0,0)"></span><span style=3D"color:rgb(0,0,136)">u=
sing</span><span style=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rg=
b(102,0,102)">ConstPropagator</span><span style=3D"color:rgb(0,0,0)"> </spa=
n><span style=3D"color:rgb(102,102,0)">=3D</span><span style=3D"color:rgb(0=
,0,0)"> std</span><span style=3D"color:rgb(102,102,0)">::</span><span style=
=3D"color:rgb(0,0,0)">propagate_const</span><span style=3D"color:rgb(102,10=
2,0)"><</span><span style=3D"color:rgb(0,0,0)">std</span><span style=3D"=
color:rgb(102,102,0)">::</span><span style=3D"color:rgb(0,0,0)">shar<wbr>ed=
_ptr</span><span style=3D"color:rgb(102,102,0)"><</span><span style=3D"c=
olor:rgb(0,0,0)">T</span><span style=3D"color:rgb(102,102,0)">>>;</sp=
an><span style=3D"color:rgb(0,0,0)"><br><br></span><span style=3D"color:rgb=
(0,0,136)">const</span><span style=3D"color:rgb(0,0,0)"> </span><span style=
=3D"color:rgb(102,0,102)">ConstPropagator</span><span style=3D"color:rgb(10=
2,102,0)">&</span><span style=3D"color:rgb(0,0,0)"> immutableRef </span=
><span style=3D"color:rgb(102,102,0)">=3D</span><span style=3D"color:rgb(0,=
0,0)"> </span><span style=3D"color:rgb(102,102,0)">...</span><span style=3D=
"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,102,0)">;</span><sp=
an style=3D"color:rgb(0,0,0)"><br>std</span><span style=3D"color:rgb(102,10=
2,0)">::</span><span style=3D"color:rgb(0,0,0)">shared_ptr</span><span styl=
e=3D"color:rgb(102,102,0)"><</span><span style=3D"color:rgb(0,0,0)">T</s=
pan><span style=3D"color:rgb(102,102,0)">></span><span style=3D"color:rg=
b(0,0,0)"> mutableRef </span><span style=3D"color:rgb(102,102,0)">=3D</span=
><span style=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(0,0,136)=
">const_cast</span><span style=3D"color:rgb(102,102,0)"><</span><span st=
yle=3D"color:rgb(102,0,102)">ConstPropagator</span><span style=3D"color:rgb=
(102,102,0)">*>(</span><span style=3D"color:rgb(0,0,0)"> </span><span st=
yle=3D"color:rgb(102,102,0)">&</span><span style=3D"color:rgb(0,0,0)">i=
mmutableRef </span><span style=3D"color:rgb(102,102,0)">)-></span><span =
style=3D"color:rgb(0,0,0)">get_underlying</span><span style=3D"color:rgb(10=
2,102,0)">();</span></div></code></div></div><span><font color=3D"#888888">
<p></p>
-- <br>
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br>
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"javascript:" target=3D"_blank" gdf-obfuscated-mailto=3D"=
tgUVZY00BwAJ" rel=3D"nofollow" onmousedown=3D"this.href=3D'javascript:&=
#39;;return true;" onclick=3D"this.href=3D'javascript:';return true=
;">std-proposal...@<wbr>isocpp.org</a>.<br>
To post to this group, send email to <a href=3D"javascript:" target=3D"_bla=
nk" gdf-obfuscated-mailto=3D"tgUVZY00BwAJ" rel=3D"nofollow" onmousedown=3D"=
this.href=3D'javascript:';return true;" onclick=3D"this.href=3D'=
;javascript:';return true;">std-pr...@isocpp.org</a>.<br>
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/9ad3f22a-a619-4488-bf1e-303641c968b9%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter" target=3D"_blank" =
rel=3D"nofollow" onmousedown=3D"this.href=3D'https://groups.google.com/=
a/isocpp.org/d/msgid/std-proposals/9ad3f22a-a619-4488-bf1e-303641c968b9%40i=
socpp.org?utm_medium\x3demail\x26utm_source\x3dfooter';return true;" on=
click=3D"this.href=3D'https://groups.google.com/a/isocpp.org/d/msgid/st=
d-proposals/9ad3f22a-a619-4488-bf1e-303641c968b9%40isocpp.org?utm_medium\x3=
demail\x26utm_source\x3dfooter';return true;">https://groups.google.com=
/a/<wbr>isocpp.org/d/msgid/std-<wbr>proposals/9ad3f22a-a619-4488-<wbr>bf1e-=
303641c968b9%40isocpp.org</a><wbr>.<br>
</font></span></blockquote></div><br></div></div>
</blockquote></div>
<p></p>
-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/189d5497-36bb-4c9c-8992-13d7eaa36b6b%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/189d5497-36bb-4c9c-8992-13d7eaa36b6b=
%40isocpp.org</a>.<br />
------=_Part_275_170315023.1487952368946--
------=_Part_274_1515010948.1487952368943--
.
Author: Jonathan Coe <jonathanbcoe@gmail.com>
Date: Fri, 24 Feb 2017 16:32:24 +0000
Raw View
--Apple-Mail-8A6462DC-6606-4EF4-BD97-7ABDD215CCAC
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
> On 24 Feb 2017, at 16:06, Hadrien Grasland <hadrien.grasland@gmail.com> w=
rote:
>=20
>=20
>=20
> Le mercredi 22 f=C3=A9vrier 2017 09:56:10 UTC+1, Jonathan Coe a =C3=A9cri=
t :
>>=20
>>=20
>> On 21 February 2017 at 14:54, Hadrien Grasland wrote:
>>> Hi everyone,
>>>=20
>>> I am not sure what this group's policy is regarding resurrection of old=
threads, so I have decided to create a new one. But I would like to second=
two comments that were previously made on the propagate_const proposal of =
N4617:
>>>=20
>>> 1/ That it should be possible to copy a propagate_const<T> wrapper ( ht=
tps://groups.google.com/a/isocpp.org/forum/#!searchin/std-proposals/propaga=
te_const/std-proposals/1uDKcA9bssU/AZ-aQNAZAAAJ ).
>>> 2/ That propagate_const<T> should be convertible to a const-correct for=
m of the underlying pointer type ( https://groups.google.com/a/isocpp.org/f=
orum/#!searchin/std-proposals/propagate_const/std-proposals/7rSMtvQVASk/niL=
SKtkcBwAJ ).
>>>=20
>>> Let me motivate, through some concrete use cases, why I think that both=
of these changes are necessary.
>>>=20
>>>=20
>>> Need for shallow copy operations
>>>=20
>>> There are many reasons to use pointers in C++. A very common use case, =
however, is dynamic polymorphism:
>>> Different implementations of a virtual interface may have different siz=
es
>>> C++, in its commendable quest for zero-cost abstraction, does not provi=
de native support for dynamically sized types
>>> When combined, these two language design choices lead to the conclusion=
that if the set of implementations of an interface is not known as compile=
time, polymorphism intrinsically requires pointer indirection in C++
>>> For this use case, pointer constness semantics are inadequate. If you u=
se a pointer-to-const, you cannot modify the target object, which is often =
fine but tends to be excessively limitating for some use cases, particularl=
y when implementing constructors and factory functions. If, on the other ha=
nd, you use a pointer-to-mutable, you open a const-correctness hole by allo=
wing yourself to mutate a const object without any compiler warning. Quite =
frankly, none of these options are very appealing.
>>>=20
>>> // Pointer-to-const restricts some valid use cases of polymorphism
>>> class FirstTry
>>> {
>>> private:
>>> std::unique_ptr<const Interface> m_ptr;
>>>=20
>>> public:
>>> FirstTry()
>>> : m_ptr{ std::make_unique<Implementation>( ... ) }
>>> {
>>> m_ptr->setParent( *this ); // ERROR: Can't mutate through a const =
pointer!
>>> }
>>> };
>>>=20
>>>=20
>>> // Pointer-to-mutable breaks const correctness of polymorphism
>>> class SecondTry
>>> {
>>> private:
>>> std::unique_ptr<Interface> m_ptr;
>>>=20
>>> public:
>>> SecondTry()
>>> : m_ptr{ std::make_unique<Implementation>( ... ) }
>>> {
>>> m_ptr->setParent( *this ); // This is now okay...
>>> }
>>>=20
>>> int someRandomAccessor() const {
>>> m_ptr->setSomething(); // ...but unfortunately, this is okay too :=
-(
>>> return 42;
>>> }
>>> };
>>>=20
>>> Now, if we are on the same wavelength, and I will assume in the followi=
ng that this is the case, this is precisely the kind of issue that propagat=
e_const is designed to help with.
>>>=20
>>> Let us now turn our attention to copy operations. Ideally, polymorphic =
objects should be deep-copyable, just like any regular value type. Unfortun=
ately, in another application of the zero cost abstraction principle, C++ p=
rovides no easy way to do this. Anyone who wants to combine value semantics=
and dynamic polymorphism needs to provide explicit support for deep copies=
across the entire class hierarchy, which is cumbersome to begin with and c=
an get problematic when interacting with third-party libraries.
>>=20
>> Arthur has addressed your concerns with the same points I would have rai=
sed (thanks Arthur). On the related topic of deep-copies and const-propagat=
ion we're working on polymorphic_value which aims to tackle copies through =
class heirarchies. I'd be interested if you have a use case where one of pr=
opagate_const and polymorphic_value does not solve your problem.=20
>>=20
>> https://github.com/jbcoe/polymorphic_value/blob/master/talks/2017_1_25_c=
xx_london.md
>> https://github.com/jbcoe/polymorphic_value/blob/master/draft.md
>=20
> I love the idea of polymorphic_value. I think that once available, it sho=
uld be taught to new C++ programmers as the standard way to manipulate poly=
morphic objects, and that anyone should strongly consider using it for this=
purpose in new codebases, or whenever a mature codebase gets completely re=
written.
>=20
> One thing which I do not think polymorphic_value can do, however, is be g=
radually introduced in an existing codebase. By design, polymorphic_value h=
as to be constructed from pointer-to-derived,
Please construct it from a value, using the factory method or roll your own=
factory method. The pointer constructor is a 'sharp knife'.
> and is thus fundamentally incompatible with the massive body of existing =
C++ code that uses pointer-to-base for polymorphism.
Can you elaborate here? Maybe an example would be helpful.
> Once you introduce a polymorphic_value somewhere, you also need to rewrit=
e every other piece of client code that uses the same class hierarchy, ofte=
n including third-party libraries that you may have no easy access to. Even=
though the final code will be cleaner, the process will be even more costl=
y as adding clone() methods everywhere. And for large projects, that will j=
ust not be seen as a viable endeavour.
>=20
I've not found this to be the case though, like many things, it probably de=
pends on patterns at use in the code.
> I believe that with the modifications that I am proposing, propagate_cons=
t would be a better fit for such refactoring purposes. With it, one could p=
rogressively introduce const-correct pointer manipulation at relatively lit=
tle cost. Sure, you wouldn't have deep copies, but hey, developers of exist=
ing codebases never had them to begin with. So we aren't going to miss them=
in a context of progressive code evolution. And if error-prone shallow cop=
ies are harder to perform by accident, well, all the better.
>=20
> =20
>=20
>>>=20
>>> class Interface
>>> {
>>> public:
>>> // This kind of boilerplate must be added to every class supporting d=
eep copy...
>>> virtual std::unique_ptr<Interface> clone() const =3D 0;
>>> };
>>>=20
>>> class Implementation : public Interface
>>> {
>>> public:
>>> // ...and replicated again and again in every single non-abstract chi=
ld. No fun.
>>> std::unique_ptr<Interface> clone() const final override
>>> {
>>> return std::make_unique<Implementation>( *this );
>>> }
>>> };
>>>=20
>>> But thankfully, sometimes, we can do without a true deep copy, and sati=
sfy ourselves with a shallow copy. And in fact, for some use cases, a shall=
ow copy will even be exactly what we want:
>>> When building some variety of search acceleration structure (hashmap, n=
eighbour locator, axis-aligned-bounding box...)
>>> When many "slave objects" share a reference to some common, potentially=
large "master object"
>>> Unfortunately, even though all standard C++ pointer types make it trivi=
al to shallow-copy a polymorphic object, propagate_const gets in our way he=
re by being move only. I see this design choice as a shortcoming, because i=
t reduces the usefulness of propagate_const in many dynamic polymorphism us=
e cases where the underlying pointer type would have done just fine. For ex=
ample, without shallow copy operations...
>>> You cannot let the compiler implement a shallow copy constructor for yo=
u using "=3D default"
>>> You cannot use std::copy_if to find objects matching some predicate in =
an internal dataset
>>> You cannot easily build object search acceleration structures, such as =
hashmaps or AABBs
>>> You cannot easily share a reference to an object wrapped by propagate_c=
onst with other objects
>>> I am aware that shallow copies are possible using the get() and get_und=
erlying() operations. I do not see this as a satisfactory answer, because i=
t breaks every STL-ish algorithm that expects a copy constructor. Of course=
, I could build a propagate_const wrapper that has a copy constructor mysel=
f, and this is what I would end up doing if propagate_const were accepted i=
n the STL in its current form. But I think that the use cases that I mentio=
ned above are valid enough to warrant changing the design of propagate_cons=
t instead.
>>>=20
>>> I am aware that in order to be const-correct, a shallow copy constructo=
r for propagate_const would need to operate from a non-const reference. I a=
m fine with this tradeoff. The main reason we usually allow ourselves to ma=
ke mutable copies from const objects is because we assume the copy to be in=
dependent from the original object. This assumption is broken when making s=
hallow copies through pointer or reference types. Sure, many APIs will be b=
roken initially, but that is unavoidable when fixing old programming langua=
ge flaws. Bugs will be reported, and interfaces will be fixed, the way it's=
always been done.
>>>=20
>>>=20
>>>=20
>>> For prior art, if you look at the STL's documentation, you will find th=
at customizable algorithms that copy const data, such as std::copy_if, expl=
icitly do NOT require the user-provided functions to consume the data by co=
nst-reference. So the idea of copying from non-const is not new. The C++ co=
mmunity only needs to re-discover it.
>>>=20
>>>=20
>>>=20
>>=20
>> We considered making copies from non-const propagate_const legal but wer=
e strongly dissuaded by 3 major standard library implementers.
>=20
> Sure, they disagreed. But what were their argument for it? Was it just a =
case of "the maffia said no"?
>=20
> =20
>> =20
>>> I am also aware of the objection, made in a thread linked above, that "=
We were not confident that a non-const copy constructor would protect the u=
ser from accidental loss of const and accidental mutable shared state". As =
far as I'm concerned, this hand-waving statement was not properly justified=
.. From my perspective, the proposed alternative of using get_underlying see=
ms to be much bigger breach of const-correctness than a copy constructor th=
at operates from mutable, because from a semantic point of view, get_underl=
ying is essentially a silent const_cast that standard code analysis tools w=
on't catch.
>>>=20
>>>=20
>>>=20
>>=20
>> We added `get_underlying` as an nicer alternative to `reinterpret_cast`,=
which would give access to the underlying pointer.
>>=20
>> Our design sought to avoid silent surprise. Granted a user can do the wr=
ong thing with get_underlying but the code is written by the user, not by t=
he compiler. Forcing the user to be explicit offers some protection.
>=20
> Been there, done that. When you see all the tools that C++ and its standa=
rd library provide to help you shoot yourself in the foot, it is always ver=
y tempting to roll your own, thinking that surely it will do the job better=
.. Except usually, it doesn't. When what you want is a cast, make it a cast,=
don't try to make it look nicer by burying it in custom abstractions.
>=20
> Let's take a step back and think about this from an API design point of v=
iew. Why makes get_underlying such an poor interface?
>=20
> 1/ By returning a reference to the internal pointer object, you leak impl=
ementation details
> 2/ By leaking implementation details, you lose the ability to maintain th=
e single most important class invariant of propagate_const, which is that t=
ype-safe accesses to a const propagate_const wrapper should not give one wr=
ite access to the internal object.
>=20
> Now, I am aware that we are discussing C++ API design here. Leaking imple=
mentation details and violating class invariants is okay, as long as it can=
give me that 0.1% performance improvement in some very obscure use case th=
at someone thought about someday. One a single compiler. With the right com=
bination of flags. Until the next release. Perhaps.
>=20
> But in the case of get_underlying, you could actually leak these implemen=
tation details, if you really wanted to, without violating your class invar=
iants along the way. The fix is simple, really. Make get_underlying a non-c=
onst method of the propagate_const object, and drop get_underlying from con=
st. Be serious about interface guarantees.
>=20
> If your users come whining because they can't easily extract a pointer-to=
-mutable from a const object, try to give them the talk about const correct=
ness. If they won't listen, tell them to const_cast the propagate_const wra=
pper. But don't compromise your API just to make stupid operations more con=
venient to write. It is never worth it.
>=20
>=20
> =20
>>>=20
>>> Const-correct convertibility to the underlying pointer type
>>>=20
>>> Sometimes, one has to extract a pointer back from the propagate_const w=
rapper. I would see two main use cases for this:
>>> To feed a legacy API that is not (yet?) compatible with propagate_const=
..
>>> To turn a propagate_const<T> into a pointer-to-const, when the recipien=
t should not be able to modify to the target object
>>> The current propagate_const interface provides three ways to do this:
>>>=20
>>> Call get() and get a raw pointer
>>> Rely on an implicit cast to raw pointer, if available
>>> Use get_underlying to access the internals of propagate_const.
>>> As I'm going to elaborate, the first two operations are not always the =
right tool for the job at hand, because they fail to convey important owner=
ship information from the underlying pointer type. Whereas get_underlying, =
as currently implemented, is a flawed interface that goes directly against =
the design goals of propagate_const and should be eradicated before standar=
dization.
>>>=20
>>>=20
>>>=20
>>> As a use case, suppose that as discussed previously, I am working a "sl=
ave object", which holds a shared_ptr to an associated "master object" that=
it shares with other slaves. To improve the const-correctness of this desi=
gn, I have decided to refactor the shared_ptr into a propagate_const<shared=
_ptr>. So far, so good.
>>>=20
>>>=20
>>>=20
>>> class Master
>>> {
>>> ...
>>> };
>>>=20
>>> class Slave
>>> {
>>> private:
>>> // This was refactored from "std::shared_ptr<Master> m_master"
>>> std::propagate_const<std::shared_ptr<Master>> m_master;
>>>=20
>>> public:
>>> Slave( std::shared_ptr<Master> master )
>>> : m_master(master)
>>> {
>>> m_master->addSlave( *this );
>>> }
>>>=20
>>> ...
>>> };=20
>>> But as I proceed with the refactoring, I discover that the Slave class =
used to provide a method that shares access to its master:
>>>=20
>>> std::shared_ptr<Master> Slave::getMaster() const
>>> {
>>> return m_master;
>>> }
>>>=20
>>> This does not compile anymore. And I'm happy about that: it should neve=
r have compiled to begin with. Returning non-const access to my members fro=
m a const method definitely does not match my idea of const-correctness!
>>>=20
>>> Instead, I would like to only provide const access to the master object=
, like so:
>>>=20
>>> std::shared_ptr<const Master> Slave::getMaster() const;
>>>=20
>>> If my clients are well-behaved and do not mutate the master object, thi=
s will be a minimally invasive interface change. It will require only minor=
client rewrites, the kind that could be automated by sed or an IDE. It cou=
ld even require no client rewrite at all if I end up being lucky and have c=
lients that were using auto and friends.
>>>=20
>>> Unfortunately, the current propagate_const interface does not allow me =
to implement this method in a clean way.
>>>=20
>>=20
>> Sadly that's intended. We can't guarantee that "clients are well-behaved=
and do not mutate the master object".
>=20
> And you do not need to, that's my job. You give me a well-designed const-=
correct pointer wrapper, and I'll use it to improve the const-correctness o=
f the codebase that I'm responsible of. If I find broken client code, I wil=
l do my best to fix it myself, and send an angry e-mail to the person respo=
nsible if I don't manage. It's called maintenance.
>=20
> But when I'm going through the pain of doing this, I would like to do so =
using quality tools that give me the impression that the effort is worthwhi=
le, and won't be undermined on the day after by someone trying to do someth=
ing stupid, going through the STL documentation and thinking "oh, I know, I=
'll just use get_underlying...".
>=20
> When people start to break the thread-safety of our code by introducing m=
utation in places where it doesn't belong, I want them to explicitly write =
down the dreaded word "cast" and feel that chill going down their spine, te=
lling them that they're doing something wrong. I want them to figure out th=
at the basic design of their code is broken, and to understand that they ne=
ed to fix it before submitting it upstream. I don't want the only line of d=
efense against software chaos to be me telling them at the end, when they f=
inally submit their merge request, that I can't accept it into our codebase=
and they need to rewrite half of it.
>=20
> Good interfaces save everyone's time by making bad things look bad. Let's=
have more of them in C++.
>=20
> =20
>> =20
>>> The obvious code snippet would not work due to the lack of an appropria=
te implicit conversion:
>>>=20
>>> std::shared_ptr<const Master> Slave::getMaster() const
>>> {
>>> return m_master; // ERROR: No implicit conversion from propagate_con=
st<shared_ptr>!
>>> }
>>>=20
>>> Using get() here would be the perfect way to introduce use after free b=
ugs:
>>>=20
>>> std::shared_ptr<const Master> Slave::getMaster() const
>>> {
>>> const Master* master =3D m_master.get();
>>> return std::shared_ptr<Master>( master ); // INCORRECT: Two owners f=
or one object!
>>> }
>>>=20
>>> And using get_underlying will both break const correctness and fail to =
provide me with the correct return type:
>>>=20
>>> std::shared_ptr<const Master> Slave::getMaster() const
>>> {
>>> auto master =3D std::get_underlying( m_master ); // This is an std::=
shared_ptr<Master>
>>> return master; // ERROR: No implicit conversion to std::shared_ptr<c=
onst Master>!
>>> }
>>>=20
>>> To implement this method, in addition to the const-incorrect get_underl=
ying interface, I would also need a const cast!
>>>=20
>>> std::shared_ptr<const Master> Slave::getMaster() const
>>> {
>>> auto master =3D std::get_underlying( m_master );=20
>>> return std::const_pointer_cast<const Master>( master );
>>> }
>>>=20
>>>=20
>>> For sure, I would never want an abomination like this to pass code revi=
ew :)
>>>=20
>>>=20
>>>=20
>> Agreed.
>> =20
>>> This little thought experiment showcases two major issues with get_unde=
rlying:
>>>=20
>>> Counter to the goal of of propagate_const, it makes it trivial to viola=
te const-correctness without the compiler noticing (or, for that matter, an=
y developer or static analysis tool that is unfamiliar with the propagate_c=
onst API).
>>> By not returning the const-correct type, get_underlying can make it unn=
ecessarily hard to implement const-correct interfaces.
>>> The authors of the original propagate_const proposal were aware that th=
e get_underlying interface was suboptimal, and for this reason they opted t=
o make it less usable by hiding it as a free function instead of making it =
a proper method of propagate_const. But personnally, I would go further: dr=
op get_underlying altogether, and replace it with something that is both co=
nst-correct and respectful of pointer ownership issues.
>>>=20
>>>=20
>>>=20
>>> My counter-proposal would be to have a propagate_const method, maybe ca=
lled "share()", which enables sharing access to the data pointed by propaga=
te_const with due respect paid to both const-correctness and the underlying=
pointer's ownership semantics. In practice:
>>>=20
>>> If called on propagate_const<T*>, share() should return a T*
>>> If called on const propagate_const<T*>, share() should return a const T=
*
>>> If called on propagate_const<shared_ptr<T>>, share() should return a sh=
ared_ptr<T>
>>> If called on const propagate_const<shared_ptr<T>>, share() should retur=
n a shared_ptr<const T>
>>> share() should not be defined on propagate_const<unique_ptr<T>> since t=
hat pointer does not support sharing ownership
>>> In addition, as a convenience, whenever a share() method exists, an imp=
licit cast could be provided to the underlying pointer type as a way to mak=
e such ownership sharing more convenient and to enable things like comparis=
on of containers of shared_ptr<T> with containers of propagate_const<shared=
_ptr<T>> through standard (STL-ish) algorithms.
>>>=20
>>>=20
>>>=20
>>> If you really, positively want something like get_underlying to exist, =
it should only be applicable to non-const object. This alone would make it =
const-correct, and thus remove the need to implement it as a free function =
and be careful when using it.
>>>=20
>>>=20
>>>=20
This is a pretty convincing argument.
Perhaps a paper for Toronto?
Let me know if you'd like any input.
>>> With this simple change, shameless people who want to get their hands d=
irty and extract a mutable pointer from a const wrapper will then need to d=
o some extra work, as should be expected of them in my opinion:
>>>=20
>>>=20
>>>=20
>>> using ConstPropagator =3D std::propagate_const<std::shared_ptr<T>>;
>>>=20
>>> const ConstPropagator& immutableRef =3D ... ;
>>> std::shared_ptr<T> mutableRef =3D const_cast<ConstPropagator*>( &immuta=
bleRef )->get_underlying();
>>> --=20
>>> You received this message because you are subscribed to the Google Grou=
ps "ISO C++ Standard - Future Proposals" group.
>>> To unsubscribe from this group and stop receiving emails from it, send =
an email to std-proposal...@isocpp.org.
>>> To post to this group, send email to std-pr...@isocpp.org.
>>> To view this discussion on the web visit https://groups.google.com/a/is=
ocpp.org/d/msgid/std-proposals/9ad3f22a-a619-4488-bf1e-303641c968b9%40isocp=
p.org.
>>=20
>=20
> --=20
> You received this message because you are subscribed to the Google Groups=
"ISO C++ Standard - Future Proposals" group.
> To unsubscribe from this group and stop receiving emails from it, send an=
email to std-proposals+unsubscribe@isocpp.org.
> To post to this group, send email to std-proposals@isocpp.org.
> To view this discussion on the web visit https://groups.google.com/a/isoc=
pp.org/d/msgid/std-proposals/189d5497-36bb-4c9c-8992-13d7eaa36b6b%40isocpp.=
org.
--=20
You received this message because you are subscribed to the Google Groups "=
ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp=
..org/d/msgid/std-proposals/FE4360D5-2235-4843-95C7-259FC65E3812%40gmail.com=
..
--Apple-Mail-8A6462DC-6606-4EF4-BD97-7ABDD215CCAC
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<html><head><meta http-equiv=3D"content-type" content=3D"text/html; charset=
=3Dutf-8"></head><body dir=3D"auto"><div></div><div><br></div><div><br>On 2=
4 Feb 2017, at 16:06, Hadrien Grasland <<a href=3D"mailto:hadrien.grasla=
nd@gmail.com">hadrien.grasland@gmail.com</a>> wrote:<br><br></div><block=
quote type=3D"cite"><div><div dir=3D"ltr"><br><br>Le mercredi 22 f=C3=A9vri=
er 2017 09:56:10 UTC+1, Jonathan Coe a =C3=A9crit :<blockquote class=
=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #cc=
c solid;padding-left: 1ex;"><div dir=3D"ltr"><div><br><div class=3D"gmail_q=
uote">On 21 February 2017 at 14:54, Hadrien Grasland wrote:<br><blockquote =
class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left-width:1=
px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:=
1ex"><div dir=3D"ltr">Hi everyone,<br><br>I am not sure what this group's p=
olicy is regarding resurrection of old threads, so I have decided to create=
a new one. But I would like to second two comments that were previously ma=
de on the propagate_const proposal of N4617:<br><br>1/ That it should be po=
ssible to copy a propagate_const<T> wrapper ( <a href=3D"https://grou=
ps.google.com/a/isocpp.org/forum/#!searchin/std-proposals/propagate_const/s=
td-proposals/1uDKcA9bssU/AZ-aQNAZAAAJ" target=3D"_blank" rel=3D"nofollow" o=
nmousedown=3D"this.href=3D'https://groups.google.com/a/isocpp.org/forum/#!s=
earchin/std-proposals/propagate_const/std-proposals/1uDKcA9bssU/AZ-aQNAZAAA=
J';return true;" onclick=3D"this.href=3D'https://groups.google.com/a/isocpp=
..org/forum/#!searchin/std-proposals/propagate_const/std-proposals/1uDKcA9bs=
sU/AZ-aQNAZAAAJ';return true;">https://groups.google.com/a/<wbr>isocpp.org/=
forum/#!searchin/<wbr>std-proposals/propagate_const/<wbr>std-proposals/1uDK=
cA9bssU/AZ-<wbr>aQNAZAAAJ</a> ).<br>2/ That propagate_const<T> should=
be convertible to a const-correct form of the underlying pointer type ( <a=
href=3D"https://groups.google.com/a/isocpp.org/forum/#!searchin/std-propos=
als/propagate_const/std-proposals/7rSMtvQVASk/niLSKtkcBwAJ" target=3D"_blan=
k" rel=3D"nofollow" onmousedown=3D"this.href=3D'https://groups.google.com/a=
/isocpp.org/forum/#!searchin/std-proposals/propagate_const/std-proposals/7r=
SMtvQVASk/niLSKtkcBwAJ';return true;" onclick=3D"this.href=3D'https://group=
s.google.com/a/isocpp.org/forum/#!searchin/std-proposals/propagate_const/st=
d-proposals/7rSMtvQVASk/niLSKtkcBwAJ';return true;">https://groups.google.c=
om/a/<wbr>isocpp.org/forum/#!searchin/<wbr>std-proposals/propagate_const/<w=
br>std-proposals/7rSMtvQVASk/<wbr>niLSKtkcBwAJ</a> ).<br><br>Let me motivat=
e, through some concrete use cases, why I think that both of these changes =
are necessary.<br><br><br><font size=3D"4">Need for shallow copy operations=
</font><br><br>There are many reasons to use pointers in C++. A very common=
use case, however, is dynamic polymorphism:<br><ul><li>Different implement=
ations of a virtual interface may have different sizes</li><li>C++, in its =
commendable quest for zero-cost abstraction, does not provide native suppor=
t for dynamically sized types</li><li>When combined, these two language des=
ign choices lead to the conclusion that if the set of implementations of an=
interface is not known as compile time, polymorphism intrinsically require=
s pointer indirection in C++</li></ul>For this use case, pointer constness =
semantics are inadequate. If you use a pointer-to-const, you cannot modify =
the target object, which is often fine but tends to be excessively limitati=
ng for some use cases, particularly when implementing constructors and fact=
ory functions. If, on the other hand, you use a pointer-to-mutable, you ope=
n a const-correctness hole by allowing yourself to mutate a const object wi=
thout any compiler warning. Quite frankly, none of these options are very a=
ppealing.<br><br><div style=3D"background-color:rgb(250,250,250);border:1px=
solid rgb(187,187,187)"><code><div><span style=3D"color:rgb(136,0,0)">// P=
ointer-to-const restricts some valid use cases of polymorphism</span><span =
style=3D"color:rgb(0,0,0)"><br></span><span style=3D"color:rgb(0,0,136)">cl=
ass</span><span style=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb=
(102,0,102)">FirstTry</span><span style=3D"color:rgb(0,0,0)"><br></span><sp=
an style=3D"color:rgb(102,102,0)">{</span><span style=3D"color:rgb(0,0,0)">=
</span><br><code><span style=3D"color:rgb(0,0,136)">private</span><span sty=
le=3D"color:rgb(102,102,0)">:</span><span style=3D"color:rgb(0,0,0)"><br>&n=
bsp; std</span><span style=3D"color:rgb(102,102,0)">::</span><span style=3D=
"color:rgb(0,0,0)">unique_ptr</span><span style=3D"color:rgb(102,102,0)">&l=
t;</span><span style=3D"color:rgb(0,0,136)">const</span><span style=3D"colo=
r:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,0,102)">Interface</span>=
<span style=3D"color:rgb(102,102,0)">></span><span style=3D"color:rgb(0,=
0,0)"> m_ptr</span><span style=3D"color:rgb(102,102,0)">;</span><span style=
=3D"color:rgb(0,0,0)"><br><br></span></code><span style=3D"color:rgb(0,0,13=
6)">public</span><span style=3D"color:rgb(102,102,0)">:</span><span style=
=3D"color:rgb(0,0,0)"><br> </span><span style=3D"color:rgb(102,0,102)=
">FirstTry</span><span style=3D"color:rgb(102,102,0)">()</span><span style=
=3D"color:rgb(0,0,0)"><br> </span><span style=3D"color:rgb(102=
,102,0)">:</span><span style=3D"color:rgb(0,0,0)"> m_ptr</span><span style=
=3D"color:rgb(102,102,0)">{</span><span style=3D"color:rgb(0,0,0)"> std</sp=
an><span style=3D"color:rgb(102,102,0)">::</span><span style=3D"color:rgb(0=
,0,0)">make_unique</span><span style=3D"color:rgb(102,102,0)"><</span><s=
pan style=3D"color:rgb(102,0,102)">Implementatio<wbr>n</span><span style=3D=
"color:rgb(102,102,0)">>(</span><span style=3D"color:rgb(0,0,0)"> </span=
><span style=3D"color:rgb(102,102,0)">...</span><span style=3D"color:rgb(0,=
0,0)"> </span><span style=3D"color:rgb(102,102,0)">)</span><span style=3D"c=
olor:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,102,0)">}</span><span=
style=3D"color:rgb(0,0,0)"><br> </span><span style=3D"color:rgb(102,=
102,0)">{</span><span style=3D"color:rgb(0,0,0)"><br> m_ptr</s=
pan><span style=3D"color:rgb(102,102,0)">-></span><span style=3D"color:r=
gb(0,0,0)">setParent</span><span style=3D"color:rgb(102,102,0)">(</span><sp=
an style=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,102,0)">=
*</span><span style=3D"color:rgb(0,0,136)">this</span><span style=3D"color:=
rgb(0,0,0)"> </span><span style=3D"color:rgb(102,102,0)">);</span><span sty=
le=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(136,0,0)">//=
ERROR:</span><span style=3D"color:rgb(0,0,0)"> Can't mutate through a cons=
t pointer!<br> </span><span style=3D"color:rgb(102,102,0)">}</span><s=
pan style=3D"color:rgb(0,0,0)"><br></span><span style=3D"color:rgb(102,102,=
0)">};</span><span style=3D"color:rgb(0,0,0)"><br><br><br></span><span styl=
e=3D"color:rgb(136,0,0)">// Pointer-to-mutable breaks const correctness of =
polymorphism</span><span style=3D"color:rgb(0,0,0)"><br></span><span style=
=3D"color:rgb(0,0,136)">class</span><span style=3D"color:rgb(0,0,0)"> </spa=
n><span style=3D"color:rgb(102,0,102)">SecondTry</span><span style=3D"color=
:rgb(0,0,0)"><br></span><span style=3D"color:rgb(102,102,0)">{</span><span =
style=3D"color:rgb(0,0,0)"><br>private:<br> std::unique_ptr<Interf=
ace> m_ptr;<br><br></span><span style=3D"color:rgb(0,0,136)">public</spa=
n><span style=3D"color:rgb(102,102,0)">:</span><span style=3D"color:rgb(0,0=
,0)"><br> </span><span style=3D"color:rgb(102,0,102)">SecondTry</span=
><span style=3D"color:rgb(102,102,0)">()</span><span style=3D"color:rgb(0,0=
,0)"><br> </span><span style=3D"color:rgb(102,102,0)">:</span>=
<span style=3D"color:rgb(0,0,0)"> m_ptr</span><span style=3D"color:rgb(102,=
102,0)">{</span><span style=3D"color:rgb(0,0,0)"> std</span><span style=3D"=
color:rgb(102,102,0)">::</span><span style=3D"color:rgb(0,0,0)">make_unique=
</span><span style=3D"color:rgb(102,102,0)"><</span><span style=3D"color=
:rgb(102,0,102)">Implementatio<wbr>n</span><span style=3D"color:rgb(102,102=
,0)">>(</span><span style=3D"color:rgb(0,0,0)"> </span><span style=3D"co=
lor:rgb(102,102,0)">...</span><span style=3D"color:rgb(0,0,0)"> </span><spa=
n style=3D"color:rgb(102,102,0)">)</span><span style=3D"color:rgb(0,0,0)"> =
</span><span style=3D"color:rgb(102,102,0)">}</span><span style=3D"color:rg=
b(0,0,0)"><br> </span><span style=3D"color:rgb(102,102,0)">{</span><s=
pan style=3D"color:rgb(0,0,0)"><br> m_ptr</span><span style=3D=
"color:rgb(102,102,0)">-></span><span style=3D"color:rgb(0,0,0)">setPare=
nt</span><span style=3D"color:rgb(102,102,0)">(</span><span style=3D"color:=
rgb(0,0,0)"> </span><span style=3D"color:rgb(102,102,0)">*</span><span styl=
e=3D"color:rgb(0,0,136)">this</span><span style=3D"color:rgb(0,0,0)"> </spa=
n><span style=3D"color:rgb(102,102,0)">);</span><span style=3D"color:rgb(0,=
0,0)"> </span><span style=3D"color:rgb(136,0,0)">// This is now okay.=
...</span><span style=3D"color:rgb(0,0,0)"><br> </span><span style=3D"=
color:rgb(102,102,0)">}</span><span style=3D"color:rgb(0,0,0)"><br><br>&nbs=
p; </span><span style=3D"color:rgb(0,0,136)">int</span><span style=3D"color=
:rgb(0,0,0)"> someRandomAccessor</span><span style=3D"color:rgb(102,102,0)"=
>()</span><span style=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb=
(0,0,136)">const</span><span style=3D"color:rgb(0,0,0)"> </span><span style=
=3D"color:rgb(102,102,0)">{</span><span style=3D"color:rgb(0,0,0)"><br>&nbs=
p; m_ptr</span><span style=3D"color:rgb(102,102,0)">-></span><spa=
n style=3D"color:rgb(0,0,0)">setSomething</span><span style=3D"color:rgb(10=
2,102,0)">();</span><span style=3D"color:rgb(0,0,0)"> </span><span st=
yle=3D"color:rgb(136,0,0)">// ...but unfortunately, this is okay too :-(</s=
pan><span style=3D"color:rgb(0,0,0)"><br> </span><span style=
=3D"color:rgb(0,0,136)">return</span><span style=3D"color:rgb(0,0,0)"> </sp=
an><span style=3D"color:rgb(0,102,102)">42</span><span style=3D"color:rgb(1=
02,102,0)">;</span><span style=3D"color:rgb(0,0,0)"><br> </span><span=
style=3D"color:rgb(102,102,0)">}</span><span style=3D"color:rgb(0,0,0)"><b=
r>};</span><span style=3D"color:rgb(0,0,0)"><br></span></div></code></div><=
br>Now, if we are on the same wavelength, and I will assume in the followin=
g that this is the case, this is precisely the kind of issue that propagate=
_const is designed to help with.<br><br>Let us now turn our attention to co=
py operations. Ideally, polymorphic objects should be deep-copyable, just l=
ike any regular value type. Unfortunately, in another application of the ze=
ro cost abstraction principle, C++ provides no easy way to do this. Anyone =
who wants to combine value semantics and dynamic polymorphism needs to prov=
ide explicit support for deep copies across the entire class hierarchy, whi=
ch is cumbersome to begin with and can get problematic when interacting wit=
h third-party libraries.<br></div></blockquote><div><br></div><div>Arthur h=
as addressed your concerns with the same points I would have raised (thanks=
Arthur). On the related topic of deep-copies and const-propagation we're w=
orking on polymorphic_value which aims to tackle copies through class heira=
rchies. I'd be interested if you have a use case where one of propagate_con=
st and polymorphic_value does not solve your problem. </div><div><br><=
/div><div><a href=3D"https://github.com/jbcoe/polymorphic_value/blob/master=
/talks/2017_1_25_cxx_london.md" target=3D"_blank" rel=3D"nofollow" onmoused=
own=3D"this.href=3D'https://www.google.com/url?q\x3dhttps%3A%2F%2Fgithub.co=
m%2Fjbcoe%2Fpolymorphic_value%2Fblob%2Fmaster%2Ftalks%2F2017_1_25_cxx_londo=
n.md\x26sa\x3dD\x26sntz\x3d1\x26usg\x3dAFQjCNHiVpbjR-rhERETegsvD58f1-r4Hw';=
return true;" onclick=3D"this.href=3D'https://www.google.com/url?q\x3dhttps=
%3A%2F%2Fgithub.com%2Fjbcoe%2Fpolymorphic_value%2Fblob%2Fmaster%2Ftalks%2F2=
017_1_25_cxx_london.md\x26sa\x3dD\x26sntz\x3d1\x26usg\x3dAFQjCNHiVpbjR-rhER=
ETegsvD58f1-r4Hw';return true;">https://github.com/jbcoe/<wbr>polymorphic_v=
alue/blob/master/<wbr>talks/2017_1_25_cxx_london.md</a><br></div><div><a hr=
ef=3D"https://github.com/jbcoe/polymorphic_value/blob/master/draft.md" targ=
et=3D"_blank" rel=3D"nofollow" onmousedown=3D"this.href=3D'https://www.goog=
le.com/url?q\x3dhttps%3A%2F%2Fgithub.com%2Fjbcoe%2Fpolymorphic_value%2Fblob=
%2Fmaster%2Fdraft.md\x26sa\x3dD\x26sntz\x3d1\x26usg\x3dAFQjCNE-iatIgiyMpvR4=
yv-KdAxgAdZuhQ';return true;" onclick=3D"this.href=3D'https://www.google.co=
m/url?q\x3dhttps%3A%2F%2Fgithub.com%2Fjbcoe%2Fpolymorphic_value%2Fblob%2Fma=
ster%2Fdraft.md\x26sa\x3dD\x26sntz\x3d1\x26usg\x3dAFQjCNE-iatIgiyMpvR4yv-Kd=
AxgAdZuhQ';return true;">https://github.com/jbcoe/<wbr>polymorphic_value/bl=
ob/master/<wbr>draft.md</a></div></div></div></div></blockquote><div><br>I =
love the idea of polymorphic_value. I think that once available, it should =
be taught to new C++ programmers as the standard way to manipulate polymorp=
hic objects, and that anyone should strongly consider using it for this pur=
pose in new codebases, or whenever a mature codebase gets completely rewrit=
ten.<br><br>One thing which I do not think polymorphic_value can do, howeve=
r, is be gradually introduced in an existing codebase. By design, polymorph=
ic_value has to be constructed from pointer-to-derived, </div></div></div><=
/blockquote><div><br></div><div>Please construct it from a value, using the=
factory method or roll your own factory method. The pointer constructor is=
a 'sharp knife'.</div><div><br></div><br><blockquote type=3D"cite"><div><d=
iv dir=3D"ltr"><div>and is thus fundamentally incompatible with the massive=
body of existing C++ code that uses pointer-to-base for polymorphism. </di=
v></div></div></blockquote><div><br></div><div>Can you elaborate here? Mayb=
e an example would be helpful.</div><br><blockquote type=3D"cite"><div><div=
dir=3D"ltr"><div>Once you introduce a polymorphic_value somewhere, you als=
o need to rewrite every other piece of client code that uses the same class=
hierarchy, often including third-party libraries that you may have no easy=
access to. Even though the final code will be cleaner, the process will be=
even more costly as adding clone() methods everywhere. And for large proje=
cts, that will just not be seen as a viable endeavour.<br><br></div></div><=
/div></blockquote><div><br></div><div>I've not found this to be the case th=
ough, like many things, it probably depends on patterns at use in the code.=
</div><br><blockquote type=3D"cite"><div><div dir=3D"ltr"><div>I believe th=
at with the modifications that I am proposing, propagate_const would be a b=
etter fit for such refactoring purposes. With it, one could progressively i=
ntroduce const-correct pointer manipulation at relatively little cost. Sure=
, you wouldn't have deep copies, but hey, developers of existing codebases =
never had them to begin with. So we aren't going to miss them in a context =
of progressive code evolution. And if error-prone shallow copies are harder=
to perform by accident, well, all the better.<br><br> <br><br></div><=
blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;bord=
er-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div><div clas=
s=3D"gmail_quote"><blockquote class=3D"gmail_quote" style=3D"margin:0px 0px=
0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-=
left-style:solid;padding-left:1ex"><div dir=3D"ltr"><br><div style=3D"backg=
round-color:rgb(250,250,250);border:1px solid rgb(187,187,187)"><code><div>=
<span style=3D"color:rgb(0,0,136)">class</span><span style=3D"color:rgb(0,0=
,0)"> </span><span style=3D"color:rgb(102,0,102)">Interface</span><span sty=
le=3D"color:rgb(0,0,0)"><br></span><span style=3D"color:rgb(102,102,0)">{</=
span><span style=3D"color:rgb(0,0,0)"><br></span><span style=3D"color:rgb(0=
,0,136)">public</span><span style=3D"color:rgb(102,102,0)">:</span><span st=
yle=3D"color:rgb(0,0,0)"><br> </span><span style=3D"color:rgb(136,0,0=
)">// This kind of boilerplate must be added to every class supporting deep=
copy...</span><span style=3D"color:rgb(0,0,0)"><br> </span><span sty=
le=3D"color:rgb(0,0,136)">virtual</span><span style=3D"color:rgb(0,0,0)"> s=
td</span><span style=3D"color:rgb(102,102,0)">::</span><span style=3D"color=
:rgb(0,0,0)">unique_ptr</span><span style=3D"color:rgb(102,102,0)"><</sp=
an><span style=3D"color:rgb(102,0,102)">Interface</span><span style=3D"colo=
r:rgb(102,102,0)">></span><span style=3D"color:rgb(0,0,0)"> clone</span>=
<span style=3D"color:rgb(102,102,0)">()</span><span style=3D"color:rgb(0,0,=
0)"> </span><span style=3D"color:rgb(0,0,136)">const</span><span style=3D"c=
olor:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,102,0)">=3D</span><sp=
an style=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(0,102,102)">=
0</span><span style=3D"color:rgb(102,102,0)">;</span><span style=3D"color:r=
gb(0,0,0)"><br></span><span style=3D"color:rgb(102,102,0)">};</span><span s=
tyle=3D"color:rgb(0,0,0)"><br><br></span><span style=3D"color:rgb(0,0,136)"=
>class</span><span style=3D"color:rgb(0,0,0)"> </span><span style=3D"color:=
rgb(102,0,102)">Implementation</span><span style=3D"color:rgb(0,0,0)"> </sp=
an><span style=3D"color:rgb(102,102,0)">:</span><span style=3D"color:rgb(0,=
0,0)"> </span><span style=3D"color:rgb(0,0,136)">public</span><span style=
=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,0,102)">Interfac=
e</span><span style=3D"color:rgb(0,0,0)"><br></span><span style=3D"color:rg=
b(102,102,0)">{</span><span style=3D"color:rgb(0,0,0)"><br></span><span sty=
le=3D"color:rgb(0,0,136)">public</span><span style=3D"color:rgb(102,102,0)"=
>:</span><span style=3D"color:rgb(0,0,0)"><br> </span><span style=3D"=
color:rgb(136,0,0)">// ...and replicated again and again in every single no=
n-abstract child. No fun.</span><span style=3D"color:rgb(0,0,0)"><br> =
std</span><span style=3D"color:rgb(102,102,0)">::</span><span style=3D"col=
or:rgb(0,0,0)">unique_ptr</span><span style=3D"color:rgb(102,102,0)"><</=
span><span style=3D"color:rgb(102,0,102)">Interface</span><span style=3D"co=
lor:rgb(102,102,0)">></span><span style=3D"color:rgb(0,0,0)"> clone</spa=
n><span style=3D"color:rgb(102,102,0)">()</span><span style=3D"color:rgb(0,=
0,0)"> </span><span style=3D"color:rgb(0,0,136)">const</span><span style=3D=
"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(0,0,136)">final</span><=
span style=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(0,0,136)">=
override</span><span style=3D"color:rgb(0,0,0)"><br> </span><span sty=
le=3D"color:rgb(102,102,0)">{</span><span style=3D"color:rgb(0,0,0)"><br>&n=
bsp; </span><span style=3D"color:rgb(0,0,136)">return</span><span st=
yle=3D"color:rgb(0,0,0)"> std</span><span style=3D"color:rgb(102,102,0)">::=
</span><span style=3D"color:rgb(0,0,0)">make_unique</span><span style=3D"co=
lor:rgb(102,102,0)"><</span><span style=3D"color:rgb(102,0,102)">Impleme=
ntatio<wbr>n</span><span style=3D"color:rgb(102,102,0)">>(</span><span s=
tyle=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,102,0)">*</s=
pan><span style=3D"color:rgb(0,0,136)">this</span><span style=3D"color:rgb(=
0,0,0)"> </span><span style=3D"color:rgb(102,102,0)">);</span><span style=
=3D"color:rgb(0,0,0)"><br> </span><span style=3D"color:rgb(102,102,0)=
">}</span><span style=3D"color:rgb(0,0,0)"><br></span><span style=3D"color:=
rgb(102,102,0)">};</span></div></code></div><br>But thankfully, sometimes, =
we can do without a true deep copy, and satisfy ourselves with a shallow co=
py. And in fact, for some use cases, a shallow copy will even be exactly wh=
at we want:<br><ul><li>When building some variety of search acceleration st=
ructure (hashmap, neighbour locator, axis-aligned-bounding box...)</li><li>=
When many "slave objects" share a reference to some common, potentially lar=
ge "master object"</li></ul>Unfortunately, even though all standard C++ poi=
nter types make it trivial to shallow-copy a polymorphic object, propagate_=
const gets in our way here by being move only. I see this design choice as =
a shortcoming, because it reduces the usefulness of propagate_const in many=
dynamic polymorphism use cases where the underlying pointer type would hav=
e done just fine. For example, without shallow copy operations...<br><ul><l=
i>You cannot let the compiler implement a shallow copy constructor for you =
using "=3D default"<br></li><li>You cannot use std::copy_if to find objects=
matching some predicate in an internal dataset</li><li>You cannot easily b=
uild object search acceleration structures, such as hashmaps or AABBs</li><=
li>You cannot easily share a reference to an object wrapped by propagate_co=
nst with other objects</li></ul><p>I am aware that shallow copies are possi=
ble using the get() and get_underlying() operations. I do not see this as a=
satisfactory answer, because it breaks every STL-ish algorithm that expect=
s a copy constructor. Of course, I could build a propagate_const wrapper th=
at has a copy constructor myself, and this is what I would end up doing if =
propagate_const were accepted in the STL in its current form. But I think t=
hat the use cases that I mentioned above are valid enough to warrant changi=
ng the design of propagate_const instead.</p></div></blockquote><blockquote=
class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left-width:=
1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left=
:1ex"><div dir=3D"ltr"><p></p><p>I am aware that in order to be const-corre=
ct, a shallow copy constructor for propagate_const would need to operate fr=
om a non-const reference. I am fine with this tradeoff. The main reason we =
usually allow ourselves to make mutable copies from const objects is becaus=
e we assume the copy to be independent from the original object. This assum=
ption is broken when making shallow copies through pointer or reference typ=
es. Sure, many APIs will be broken initially, but that is unavoidable when =
fixing old programming language flaws. Bugs will be reported, and interface=
s will be fixed, the way it's always been done.<br></p><p><br></p><p>For pr=
ior art, if you look at the STL's documentation, you will find that customi=
zable algorithms that copy const data, such as std::copy_if, explicitly do =
NOT require the user-provided functions to consume the data by const-refere=
nce. So the idea of copying from non-const is not new. The C++ community on=
ly needs to re-discover it.<br></p><p><br></p></div></blockquote><div><br><=
/div><div>We considered making copies from non-const propagate_const legal =
but were strongly dissuaded by 3 major standard library implementers.</div>=
</div></div></div></blockquote><div><br>Sure, they disagreed. But what were=
their argument for it? Was it just a case of "the maffia said no"?<br><br>=
</div><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-lef=
t: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><=
div><div class=3D"gmail_quote"><div> </div><blockquote class=3D"=
gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left-width:1px;border=
-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div=
dir=3D"ltr"><p></p><p>I am also aware of the objection, made in a thread l=
inked above, that "We were not confident that a non-const copy constructor =
would protect=20
the user from accidental loss of const and accidental mutable shared=20
state". As far as I'm concerned, this hand-waving statement was not properl=
y justified. From my perspective, the proposed alternative of using get_und=
erlying seems to be much bigger breach of const-correctness than a copy con=
structor that operates from mutable, because from a semantic point of view,=
get_underlying is essentially a silent const_cast that standard code analy=
sis tools won't catch.</p><p><br></p></div></blockquote><div><br></div><div=
>We added `get_underlying` as an nicer alternative to `reinterpret_cast`, w=
hich would give access to the underlying pointer.<br></div><div><br></div><=
div>Our design sought to avoid silent surprise. Granted a user can do the w=
rong thing with get_underlying but the code is written by the user, not by =
the compiler. Forcing the user to be explicit offers some protection.</div>=
</div></div></div></blockquote><div><br>Been there, done that. When you see=
all the tools that C++ and its standard library provide to help you shoot =
yourself in the foot, it is always very tempting to roll your own, thinking=
that surely it will do the job better. Except usually, it doesn't. When wh=
at you want is a cast, make it a cast, don't try to make it look nicer by b=
urying it in custom abstractions.<br><br>Let's take a step back and think a=
bout this from an API design point of view. Why makes get_underlying such a=
n poor interface?<br><br>1/ By returning a reference to the internal pointe=
r object, you leak implementation details<br>2/ By leaking implementation d=
etails, you lose the ability to maintain the single most important class in=
variant of propagate_const, which is that type-safe accesses to a const pro=
pagate_const wrapper should not give one write access to the internal objec=
t.<br><br>Now, I am aware that we are discussing C++ API design here. Leaki=
ng implementation details and violating class invariants is okay, as long a=
s it can give me that 0.1% performance improvement in some very obscure use=
case that someone thought about someday. One a single compiler. With the r=
ight combination of flags. Until the next release. Perhaps.<br><br>But in t=
he case of get_underlying, you could actually leak these implementation det=
ails, if you really wanted to, without violating your class invariants alon=
g the way. The fix is simple, really. Make get_underlying a non-const metho=
d of the propagate_const object, and drop get_underlying from const. Be ser=
ious about interface guarantees.<br><br>If your users come whining because =
they can't easily extract a pointer-to-mutable from a const object, try to =
give them the talk about const correctness. If they won't listen, tell them=
to const_cast the propagate_const wrapper. But don't compromise your API j=
ust to make stupid operations more convenient to write. It is never worth i=
t.<br><br><br> <br></div><blockquote class=3D"gmail_quote" style=3D"ma=
rgin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">=
<div dir=3D"ltr"><div><div class=3D"gmail_quote"><blockquote class=3D"gmail=
_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left=
-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div dir=
=3D"ltr"><p></p><br><font size=3D"4">Const-correct convertibility to the un=
derlying pointer type</font><br><br>Sometimes, one has to extract a pointer=
back from the propagate_const wrapper. I would see two main use cases for =
this:<br><ul><li>To feed a legacy API that is not (yet?) compatible with pr=
opagate_const.<br></li><li>To turn a propagate_const<T> into a pointe=
r-to-const, when the recipient should not be able to modify to the target o=
bject</li></ul><p>The current propagate_const interface provides three ways=
to do this:</p><ul><li>Call get() and get a raw pointer</li><li>Rely on an=
implicit cast to raw pointer, if available</li><li>Use get_underlying to a=
ccess the internals of propagate_const.</li></ul><p>As I'm going to elabora=
te, the first two operations are not always the right tool for the job at h=
and, because they fail to convey important ownership information from the u=
nderlying pointer type. Whereas get_underlying, as currently implemented, i=
s a flawed interface that goes directly against the design goals of propaga=
te_const and should be eradicated before standardization.</p><p><br></p><p>=
As a use case, suppose that as discussed previously, I am working a "slave =
object", which holds a shared_ptr to an associated "master object" that it =
shares with other slaves. To improve the const-correctness of this design, =
I have decided to refactor the shared_ptr into a propagate_const<shared_=
ptr>. So far, so good.<br></p><p><br></p><p></p><div style=3D"background=
-color:rgb(250,250,250);border:1px solid rgb(187,187,187)"><code><div><span=
style=3D"color:rgb(0,0,136)">class</span><span style=3D"color:rgb(0,0,0)">=
</span><span style=3D"color:rgb(102,0,102)">Master</span><span style=3D"co=
lor:rgb(0,0,0)"><br></span><span style=3D"color:rgb(102,102,0)">{</span><sp=
an style=3D"color:rgb(0,0,0)"><br> </span><span style=3D"color:rgb(10=
2,102,0)">...</span><span style=3D"color:rgb(0,0,0)"><br></span><span style=
=3D"color:rgb(102,102,0)">};</span><span style=3D"color:rgb(0,0,0)"><br><br=
></span><span style=3D"color:rgb(0,0,136)">class</span><span style=3D"color=
:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,0,102)">Slave</span><span=
style=3D"color:rgb(0,0,0)"><br></span><span style=3D"color:rgb(102,102,0)"=
>{</span><span style=3D"color:rgb(0,0,0)"><br></span><span style=3D"color:r=
gb(0,0,136)">private</span><span style=3D"color:rgb(102,102,0)">:</span><sp=
an style=3D"color:rgb(0,0,0)"><br> </span><span style=3D"color:rgb(13=
6,0,0)">// This was refactored from "std::shared_ptr<Master> m_master=
"</span><span style=3D"color:rgb(0,0,0)"><br> std</span><span style=
=3D"color:rgb(102,102,0)">::</span><span style=3D"color:rgb(0,0,0)">propaga=
te_const</span><span style=3D"color:rgb(102,102,0)"><</span><span style=
=3D"color:rgb(0,0,0)">std</span><span style=3D"color:rgb(102,102,0)">::</sp=
an><span style=3D"color:rgb(0,0,0)">shar<wbr>ed_ptr</span><span style=3D"co=
lor:rgb(102,102,0)"><</span><span style=3D"color:rgb(102,0,102)">Master<=
/span><span style=3D"color:rgb(102,102,0)">>></span><span style=3D"co=
lor:rgb(0,0,0)"> m_master</span><span style=3D"color:rgb(102,102,0)">;</spa=
n><span style=3D"color:rgb(0,0,0)"><br><br></span><span style=3D"color:rgb(=
0,0,136)">public</span><span style=3D"color:rgb(102,102,0)">:</span><span s=
tyle=3D"color:rgb(0,0,0)"><br> </span><span style=3D"color:rgb(102,0,=
102)">Slave</span><span style=3D"color:rgb(102,102,0)">(</span><span style=
=3D"color:rgb(0,0,0)"> std</span><span style=3D"color:rgb(102,102,0)">::</s=
pan><span style=3D"color:rgb(0,0,0)">shared_ptr</span><span style=3D"color:=
rgb(102,102,0)"><</span><span style=3D"color:rgb(102,0,102)">Master</spa=
n><span style=3D"color:rgb(102,102,0)">></span><span style=3D"color:rgb(=
0,0,0)"> master </span><span style=3D"color:rgb(102,102,0)">)</span><span s=
tyle=3D"color:rgb(0,0,0)"><br> </span><span style=3D"color:rgb=
(102,102,0)">:</span><span style=3D"color:rgb(0,0,0)"> m_master</span><span=
style=3D"color:rgb(102,102,0)">(</span><span style=3D"color:rgb(0,0,0)">ma=
ster</span><span style=3D"color:rgb(102,102,0)">)</span><span style=3D"colo=
r:rgb(0,0,0)"><br> </span><span style=3D"color:rgb(102,102,0)">{</spa=
n><span style=3D"color:rgb(0,0,0)"><br> m_master</span><span s=
tyle=3D"color:rgb(102,102,0)">-></span><span style=3D"color:rgb(0,0,0)">=
addSlave</span><span style=3D"color:rgb(102,102,0)">(</span><span style=3D"=
color:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,102,0)">*</span><spa=
n style=3D"color:rgb(0,0,136)">this</span><span style=3D"color:rgb(0,0,0)">=
</span><span style=3D"color:rgb(102,102,0)">);</span><span style=3D"color:=
rgb(0,0,0)"><br> </span><span style=3D"color:rgb(102,102,0)">}</span>=
<span style=3D"color:rgb(0,0,0)"><br><br> ...<br></span><span style=
=3D"color:rgb(102,102,0)">};</span><span style=3D"font-family:arial,sans-se=
rif;background-color:rgb(255,255,255)"> </span></div></code></div></di=
v></blockquote><blockquote class=3D"gmail_quote" style=3D"margin:0px 0px 0p=
x 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-lef=
t-style:solid;padding-left:1ex"><div dir=3D"ltr"><p></p>But as I proceed wi=
th the refactoring, I discover that the Slave class used to provide a metho=
d that shares access to its master:<br><br><div style=3D"background-color:r=
gb(250,250,250);border:1px solid rgb(187,187,187)"><code><div><span style=
=3D"color:rgb(0,0,0)">std</span><span style=3D"color:rgb(102,102,0)">::</sp=
an><span style=3D"color:rgb(0,0,0)">shared_ptr</span><span style=3D"color:r=
gb(102,102,0)"><</span><span style=3D"color:rgb(102,0,102)">Master</span=
><span style=3D"color:rgb(102,102,0)">></span><span style=3D"color:rgb(0=
,0,0)"> </span><span style=3D"color:rgb(102,0,102)">Slave</span><span style=
=3D"color:rgb(102,102,0)">::</span><span style=3D"color:rgb(0,0,0)">getMast=
er</span><span style=3D"color:rgb(102,102,0)">()</span><span style=3D"color=
:rgb(0,0,0)"> </span><span style=3D"color:rgb(0,0,136)">const</span><span s=
tyle=3D"color:rgb(0,0,0)"><br></span><span style=3D"color:rgb(102,102,0)">{=
</span><span style=3D"color:rgb(0,0,0)"><br> </span><span style=3D"co=
lor:rgb(0,0,136)">return</span><span style=3D"color:rgb(0,0,0)"> m_master</=
span><span style=3D"color:rgb(102,102,0)">;</span><span style=3D"color:rgb(=
0,0,0)"><br></span><span style=3D"color:rgb(102,102,0)">}</span></div></cod=
e></div><br>This does not compile anymore. And I'm happy about that: it sho=
uld never have compiled to begin with. Returning non-const access to my mem=
bers from a const method definitely does not match my idea of const-correct=
ness!<br><br>Instead, I would like to only provide const access to the mast=
er object, like so:<br><br><div style=3D"background-color:rgb(250,250,250);=
border:1px solid rgb(187,187,187)"><code><div><span style=3D"color:rgb(0,0,=
0)">std</span><span style=3D"color:rgb(102,102,0)">::</span><span style=3D"=
color:rgb(0,0,0)">shared_ptr</span><span style=3D"color:rgb(102,102,0)"><=
;</span><span style=3D"color:rgb(0,0,136)">const</span><span style=3D"color=
:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,0,102)">Master</span><spa=
n style=3D"color:rgb(102,102,0)">></span><span style=3D"color:rgb(0,0,0)=
"> </span><span style=3D"color:rgb(102,0,102)">Slave</span><span style=3D"c=
olor:rgb(102,102,0)">::</span><span style=3D"color:rgb(0,0,0)">getMaster</s=
pan><span style=3D"color:rgb(102,102,0)">()</span><span style=3D"color:rgb(=
0,0,0)"> </span><span style=3D"color:rgb(0,0,136)">const</span><span style=
=3D"color:rgb(102,102,0)">;</span></div></code></div><br>If my clients are =
well-behaved and do not mutate the master object, this will be a minimally =
invasive interface change. It will require only minor client rewrites, the =
kind that could be automated by sed or an IDE. It could even require no cli=
ent rewrite at all if I end up being lucky and have clients that were using=
auto and friends.<br><br>Unfortunately, the current propagate_const interf=
ace does not allow me to implement this method in a clean way.<br><br></div=
></blockquote><div><br></div><div>Sadly that's intended. We can't guarantee=
that "clients are well-behaved and do not mutate the master object".</div>=
</div></div></div></blockquote><div><br>And you do not need to, that's my j=
ob. You give me a well-designed const-correct pointer wrapper, and I'll use=
it to improve the const-correctness of the codebase that I'm responsible o=
f. If I find broken client code, I will do my best to fix it myself, and se=
nd an angry e-mail to the person responsible if I don't manage. It's called=
maintenance.<br><br>But when I'm going through the pain of doing this, I w=
ould like to do so using quality tools that give me the impression that the=
effort is worthwhile, and won't be undermined on the day after by someone =
trying to do something stupid, going through the STL documentation and thin=
king "oh, I know, I'll just use get_underlying...".<br><br>When people star=
t to break the thread-safety of our code by introducing mutation in places =
where it doesn't belong, I want them to explicitly write down the dreaded w=
ord "cast" and feel that chill going down their spine, telling them that th=
ey're doing something wrong. I want them to figure out that the basic desig=
n of their code is broken, and to understand that they need to fix it befor=
e submitting it upstream. I don't want the only line of defense against sof=
tware chaos to be me telling them at the end, when they finally submit thei=
r merge request, that I can't accept it into our codebase and they need to =
rewrite half of it.<br><br>Good interfaces save everyone's time by making b=
ad things look bad. Let's have more of them in C++.<br><br> </div><blo=
ckquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-=
left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div><div class=
=3D"gmail_quote"><div> </div><blockquote class=3D"gmail_quote" style=
=3D"margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(20=
4,204,204);border-left-style:solid;padding-left:1ex"><div dir=3D"ltr">The o=
bvious code snippet would not work due to the lack of an appropriate implic=
it conversion:<br><br><div style=3D"background-color:rgb(250,250,250);borde=
r:1px solid rgb(187,187,187)"><code><div><span style=3D"color:rgb(0,0,0)">s=
td</span><span style=3D"color:rgb(102,102,0)">::</span><span style=3D"color=
:rgb(0,0,0)">shared_ptr</span><span style=3D"color:rgb(102,102,0)"><cons=
t </span><span style=3D"color:rgb(102,0,102)">Master</span><span style=3D"c=
olor:rgb(102,102,0)">></span><span style=3D"color:rgb(0,0,0)"> </span><s=
pan style=3D"color:rgb(102,0,102)">Slave</span><span style=3D"color:rgb(102=
,102,0)">::</span><span style=3D"color:rgb(0,0,0)">getMaster</span><span st=
yle=3D"color:rgb(102,102,0)">()</span><span style=3D"color:rgb(0,0,0)"> </s=
pan><span style=3D"color:rgb(0,0,136)">const</span><span style=3D"color:rgb=
(0,0,0)"><br></span><span style=3D"color:rgb(102,102,0)">{</span><span styl=
e=3D"color:rgb(0,0,0)"><br> </span><span style=3D"color:rgb(0,0,136)"=
>return</span><span style=3D"color:rgb(0,0,0)"> m_master</span><span style=
=3D"color:rgb(102,102,0)">;</span><span style=3D"color:rgb(0,0,0)"> /=
/ ERROR: No implicit conversion from propagate_const<shared_ptr>!<br>=
</span><span style=3D"color:rgb(102,102,0)">}</span></div></code></div><br>=
Using get() here would be the perfect way to introduce use after free bugs:=
<br><br><div style=3D"background-color:rgb(250,250,250);border:1px solid rg=
b(187,187,187)"><code><div><span style=3D"color:rgb(0,0,0)">std</span><span=
style=3D"color:rgb(102,102,0)">::</span><span style=3D"color:rgb(0,0,0)">s=
hared_ptr</span><span style=3D"color:rgb(102,102,0)"><const </span><span=
style=3D"color:rgb(102,0,102)">Master</span><span style=3D"color:rgb(102,1=
02,0)">></span><span style=3D"color:rgb(0,0,0)"> </span><span style=3D"c=
olor:rgb(102,0,102)">Slave</span><span style=3D"color:rgb(102,102,0)">::</s=
pan><span style=3D"color:rgb(0,0,0)">getMaster</span><span style=3D"color:r=
gb(102,102,0)">()</span><span style=3D"color:rgb(0,0,0)"> </span><span styl=
e=3D"color:rgb(0,0,136)">const</span><span style=3D"color:rgb(0,0,0)"><br><=
/span><span style=3D"color:rgb(102,102,0)">{</span><span style=3D"color:rgb=
(0,0,0)"><br> const Master* master =3D m_master.get();<br> </sp=
an><span style=3D"color:rgb(0,0,136)">return</span><span style=3D"color:rgb=
(0,0,0)"> std::shared_ptr<Master>( master</span><span style=3D"color:=
rgb(102,102,0)"> );</span><span style=3D"color:rgb(0,0,0)"> // INCORR=
ECT: Two owners for one object!<br></span><span style=3D"color:rgb(102,102,=
0)">}</span></div></code></div><br>And using get_underlying will both break=
const correctness and fail to provide me with the correct return type:<br>=
<br><div style=3D"background-color:rgb(250,250,250);border:1px solid rgb(18=
7,187,187)"><code><div><span style=3D"color:rgb(0,0,0)">std</span><span sty=
le=3D"color:rgb(102,102,0)">::</span><span style=3D"color:rgb(0,0,0)">share=
d_ptr</span><span style=3D"color:rgb(102,102,0)"><const </span><span sty=
le=3D"color:rgb(102,0,102)">Master</span><span style=3D"color:rgb(102,102,0=
)">></span><span style=3D"color:rgb(0,0,0)"> </span><span style=3D"color=
:rgb(102,0,102)">Slave</span><span style=3D"color:rgb(102,102,0)">::</span>=
<span style=3D"color:rgb(0,0,0)">getMaster</span><span style=3D"color:rgb(1=
02,102,0)">()</span><span style=3D"color:rgb(0,0,0)"> </span><span style=3D=
"color:rgb(0,0,136)">const</span><span style=3D"color:rgb(0,0,0)"><br></spa=
n><span style=3D"color:rgb(102,102,0)">{</span><span style=3D"color:rgb(0,0=
,0)"><br> auto master =3D std::get_underlying( m_master ); // T=
his is an std::shared_ptr<Master><br> </span><span style=3D"col=
or:rgb(0,0,136)">return</span><span style=3D"color:rgb(0,0,0)"> master;&nbs=
p; // ERROR: No implicit conversion to std::shared_ptr<const Master>!=
</span><span style=3D"color:rgb(0,0,0)"><br></span><span style=3D"color:rgb=
(102,102,0)">}</span></div></code></div><br>To implement this method, in ad=
dition to the const-incorrect get_underlying interface, I would also need a=
const cast!<br><br><div style=3D"background-color:rgb(250,250,250);border:=
1px solid rgb(187,187,187)"><code><div><span style=3D"color:rgb(0,0,0)">std=
</span><span style=3D"color:rgb(102,102,0)">::</span><span style=3D"color:r=
gb(0,0,0)">shared_ptr</span><span style=3D"color:rgb(102,102,0)"><const =
</span><span style=3D"color:rgb(102,0,102)">Master</span><span style=3D"col=
or:rgb(102,102,0)">></span><span style=3D"color:rgb(0,0,0)"> </span><spa=
n style=3D"color:rgb(102,0,102)">Slave</span><span style=3D"color:rgb(102,1=
02,0)">::</span><span style=3D"color:rgb(0,0,0)">getMaster</span><span styl=
e=3D"color:rgb(102,102,0)">()</span><span style=3D"color:rgb(0,0,0)"> </spa=
n><span style=3D"color:rgb(0,0,136)">const</span><span style=3D"color:rgb(0=
,0,0)"><br></span><span style=3D"color:rgb(102,102,0)">{</span><span style=
=3D"color:rgb(0,0,0)"><br> auto master =3D std::get_underlying( m_mas=
ter ); <br> </span><span style=3D"color:rgb(0,0,136)">return</span><s=
pan style=3D"color:rgb(0,0,0)"> std::const_pointer_cast<const Master>=
( master );</span><span style=3D"color:rgb(0,0,0)"><br></span><span style=
=3D"color:rgb(102,102,0)">}</span></div></code></div><p><br></p><p>For sure=
, I would never want an abomination like this to pass code review :)</p><p>=
<br></p></div></blockquote><div>Agreed.</div><div> </div><blockquote c=
lass=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left-width:1p=
x;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1=
ex"><div dir=3D"ltr"><p></p><p>This little thought experiment showcases two=
major issues with get_underlying:</p><ul><li>Counter to the goal of of pro=
pagate_const, it makes it trivial to violate const-correctness without the =
compiler noticing (or, for that matter, any developer or static analysis to=
ol that is unfamiliar with the propagate_const API).<br></li><li>By not ret=
urning the const-correct type, get_underlying can make it unnecessarily har=
d to implement const-correct interfaces.<br></li></ul><p></p><p>The authors=
of the original propagate_const proposal were aware that the get_underlyin=
g interface was suboptimal, and for this reason they opted to make it less =
usable by hiding it as a free function instead of making it a proper method=
of propagate_const. But personnally, I would go further: drop get_underlyi=
ng altogether, and replace it with something that is both const-correct and=
respectful of pointer ownership issues.<br></p><p><br></p><p>My counter-pr=
oposal would be to have a propagate_const method, maybe called "share()", w=
hich enables sharing access to the data pointed by propagate_const with due=
respect paid to both const-correctness and the underlying pointer's owners=
hip semantics. In practice:</p><ul><li>If called on propagate_const<T*&g=
t;, share() should return a T*</li><li>If called on const propagate_const&l=
t;T*>, share() should return a const T*</li><li>If called on propagate_c=
onst<shared_ptr<T>><wbr>, share() should return a shared_ptr<=
;T></li><li>If called on const propagate_const<shared_ptr<T>>=
;<wbr>, share() should return a shared_ptr<const T></li><li>share() s=
hould not be defined on propagate_const<unique_ptr<T>> since th=
at pointer does not support sharing ownership</li></ul><p>In addition, as a=
convenience, whenever a share() method exists, an implicit cast could be p=
rovided to the underlying pointer type as a way to make such ownership shar=
ing more convenient and to enable things like comparison of containers of s=
hared_ptr<T> with containers of propagate_const<shared_ptr<T>=
;> through standard (STL-ish) algorithms.</p><p><br></p><p>If you really=
, positively want something like get_underlying to exist, it should only be=
applicable to non-const object. This alone would make it const-correct, an=
d thus remove the need to implement it as a free function and be careful wh=
en using it.</p><p><br></p></div></blockquote></div></div></div></blockquot=
e></div></div></blockquote><div>This is a pretty convincing argument.</div>=
<div>Perhaps a paper for Toronto?</div><div>Let me know if you'd like any i=
nput.</div><div><br></div><blockquote type=3D"cite"><div><div dir=3D"ltr"><=
blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;bord=
er-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div><div clas=
s=3D"gmail_quote"><blockquote class=3D"gmail_quote" style=3D"margin:0px 0px=
0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-=
left-style:solid;padding-left:1ex"><div dir=3D"ltr"><p>With this simple cha=
nge, shameless people who want to get their hands dirty and extract a mutab=
le pointer from a const wrapper will then need to do some extra work, as sh=
ould be expected of them in my opinion:</p><p><br></p><div style=3D"backgro=
und-color:rgb(250,250,250);border:1px solid rgb(187,187,187)"><code><div><s=
pan style=3D"color:rgb(0,0,0)"></span><span style=3D"color:rgb(0,0,136)">us=
ing</span><span style=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb=
(102,0,102)">ConstPropagator</span><span style=3D"color:rgb(0,0,0)"> </span=
><span style=3D"color:rgb(102,102,0)">=3D</span><span style=3D"color:rgb(0,=
0,0)"> std</span><span style=3D"color:rgb(102,102,0)">::</span><span style=
=3D"color:rgb(0,0,0)">propagate_const</span><span style=3D"color:rgb(102,10=
2,0)"><</span><span style=3D"color:rgb(0,0,0)">std</span><span style=3D"=
color:rgb(102,102,0)">::</span><span style=3D"color:rgb(0,0,0)">shar<wbr>ed=
_ptr</span><span style=3D"color:rgb(102,102,0)"><</span><span style=3D"c=
olor:rgb(0,0,0)">T</span><span style=3D"color:rgb(102,102,0)">>>;</sp=
an><span style=3D"color:rgb(0,0,0)"><br><br></span><span style=3D"color:rgb=
(0,0,136)">const</span><span style=3D"color:rgb(0,0,0)"> </span><span style=
=3D"color:rgb(102,0,102)">ConstPropagator</span><span style=3D"color:rgb(10=
2,102,0)">&</span><span style=3D"color:rgb(0,0,0)"> immutableRef </span=
><span style=3D"color:rgb(102,102,0)">=3D</span><span style=3D"color:rgb(0,=
0,0)"> </span><span style=3D"color:rgb(102,102,0)">...</span><span style=3D=
"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,102,0)">;</span><sp=
an style=3D"color:rgb(0,0,0)"><br>std</span><span style=3D"color:rgb(102,10=
2,0)">::</span><span style=3D"color:rgb(0,0,0)">shared_ptr</span><span styl=
e=3D"color:rgb(102,102,0)"><</span><span style=3D"color:rgb(0,0,0)">T</s=
pan><span style=3D"color:rgb(102,102,0)">></span><span style=3D"color:rg=
b(0,0,0)"> mutableRef </span><span style=3D"color:rgb(102,102,0)">=3D</span=
><span style=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(0,0,136)=
">const_cast</span><span style=3D"color:rgb(102,102,0)"><</span><span st=
yle=3D"color:rgb(102,0,102)">ConstPropagator</span><span style=3D"color:rgb=
(102,102,0)">*>(</span><span style=3D"color:rgb(0,0,0)"> </span><span st=
yle=3D"color:rgb(102,102,0)">&</span><span style=3D"color:rgb(0,0,0)">i=
mmutableRef </span><span style=3D"color:rgb(102,102,0)">)-></span><span =
style=3D"color:rgb(0,0,0)">get_underlying</span><span style=3D"color:rgb(10=
2,102,0)">();</span></div></code></div></div><span><font color=3D"#888888">
<p></p>
-- <br>
You received this message because you are subscribed to the Google Groups "=
ISO C++ Standard - Future Proposals" group.<br>
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"javascript:" target=3D"_blank" gdf-obfuscated-mailto=3D"=
tgUVZY00BwAJ" rel=3D"nofollow" onmousedown=3D"this.href=3D'javascript:';ret=
urn true;" onclick=3D"this.href=3D'javascript:';return true;">std-proposal.=
...@<wbr>isocpp.org</a>.<br>
To post to this group, send email to <a href=3D"javascript:" target=3D"_bla=
nk" gdf-obfuscated-mailto=3D"tgUVZY00BwAJ" rel=3D"nofollow" onmousedown=3D"=
this.href=3D'javascript:';return true;" onclick=3D"this.href=3D'javascript:=
';return true;">std-pr...@isocpp.org</a>.<br>
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/9ad3f22a-a619-4488-bf1e-303641c968b9%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter" target=3D"_blank" =
rel=3D"nofollow" onmousedown=3D"this.href=3D'https://groups.google.com/a/is=
ocpp.org/d/msgid/std-proposals/9ad3f22a-a619-4488-bf1e-303641c968b9%40isocp=
p.org?utm_medium\x3demail\x26utm_source\x3dfooter';return true;" onclick=3D=
"this.href=3D'https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/=
9ad3f22a-a619-4488-bf1e-303641c968b9%40isocpp.org?utm_medium\x3demail\x26ut=
m_source\x3dfooter';return true;">https://groups.google.com/a/<wbr>isocpp.o=
rg/d/msgid/std-<wbr>proposals/9ad3f22a-a619-4488-<wbr>bf1e-303641c968b9%40i=
socpp.org</a><wbr>.<br>
</font></span></blockquote></div><br></div></div>
</blockquote></div>
<p></p>
-- <br>
You received this message because you are subscribed to the Google Groups "=
ISO C++ Standard - Future Proposals" group.<br>
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br>
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br>
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/189d5497-36bb-4c9c-8992-13d7eaa36b6b%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.goo=
gle.com/a/isocpp.org/d/msgid/std-proposals/189d5497-36bb-4c9c-8992-13d7eaa3=
6b6b%40isocpp.org</a>.<br>
</div></blockquote></body></html>
<p></p>
-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/FE4360D5-2235-4843-95C7-259FC65E3812%=
40gmail.com?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/FE4360D5-2235-4843-95C7-259FC65E3812%=
40gmail.com</a>.<br />
--Apple-Mail-8A6462DC-6606-4EF4-BD97-7ABDD215CCAC--
.
Author: Hadrien Grasland <hadrien.grasland@gmail.com>
Date: Sat, 25 Feb 2017 02:41:20 -0800 (PST)
Raw View
------=_Part_178_123620501.1488019280400
Content-Type: multipart/alternative;
boundary="----=_Part_179_278325470.1488019280402"
------=_Part_179_278325470.1488019280402
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
Le vendredi 24 f=C3=A9vrier 2017 17:32:29 UTC+1, Jonathan Coe a =C3=A9crit =
:
>
>
>
> On 24 Feb 2017, at 16:06, Hadrien Grasland <hadrien....@gmail.com=20
> <javascript:>> wrote:
>
>
>
> Le mercredi 22 f=C3=A9vrier 2017 09:56:10 UTC+1, Jonathan Coe a =C3=A9cri=
t :
>>
>>
>> On 21 February 2017 at 14:54, Hadrien Grasland wrote:
>>
>>> Hi everyone,
>>>
>>> I am not sure what this group's policy is regarding resurrection of old=
=20
>>> threads, so I have decided to create a new one. But I would like to sec=
ond=20
>>> two comments that were previously made on the propagate_const proposal =
of=20
>>> N4617:
>>>
>>> 1/ That it should be possible to copy a propagate_const<T> wrapper (=20
>>> https://groups.google.com/a/isocpp.org/forum/#!searchin/std-proposals/p=
ropagate_const/std-proposals/1uDKcA9bssU/AZ-aQNAZAAAJ=20
>>> ).
>>> 2/ That propagate_const<T> should be convertible to a const-correct for=
m=20
>>> of the underlying pointer type (=20
>>> https://groups.google.com/a/isocpp.org/forum/#!searchin/std-proposals/p=
ropagate_const/std-proposals/7rSMtvQVASk/niLSKtkcBwAJ=20
>>> ).
>>>
>>> Let me motivate, through some concrete use cases, why I think that both=
=20
>>> of these changes are necessary.
>>>
>>>
>>> Need for shallow copy operations
>>>
>>> There are many reasons to use pointers in C++. A very common use case,=
=20
>>> however, is dynamic polymorphism:
>>>
>>> - Different implementations of a virtual interface may have=20
>>> different sizes
>>> - C++, in its commendable quest for zero-cost abstraction, does not=
=20
>>> provide native support for dynamically sized types
>>> - When combined, these two language design choices lead to the=20
>>> conclusion that if the set of implementations of an interface is not=
known=20
>>> as compile time, polymorphism intrinsically requires pointer indirec=
tion in=20
>>> C++
>>>
>>> For this use case, pointer constness semantics are inadequate. If you=
=20
>>> use a pointer-to-const, you cannot modify the target object, which is o=
ften=20
>>> fine but tends to be excessively limitating for some use cases,=20
>>> particularly when implementing constructors and factory functions. If, =
on=20
>>> the other hand, you use a pointer-to-mutable, you open a const-correctn=
ess=20
>>> hole by allowing yourself to mutate a const object without any compiler=
=20
>>> warning. Quite frankly, none of these options are very appealing.
>>>
>>> // Pointer-to-const restricts some valid use cases of polymorphism
>>> class FirstTry
>>> {
>>> private:
>>> std::unique_ptr<const Interface> m_ptr;
>>>
>>> public:
>>> FirstTry()
>>> : m_ptr{ std::make_unique<Implementation>( ... ) }
>>> {
>>> m_ptr->setParent( *this ); // ERROR: Can't mutate through a const=
=20
>>> pointer!
>>> }
>>> };
>>>
>>>
>>> // Pointer-to-mutable breaks const correctness of polymorphism
>>> class SecondTry
>>> {
>>> private:
>>> std::unique_ptr<Interface> m_ptr;
>>>
>>> public:
>>> SecondTry()
>>> : m_ptr{ std::make_unique<Implementation>( ... ) }
>>> {
>>> m_ptr->setParent( *this ); // This is now okay...
>>> }
>>>
>>> int someRandomAccessor() const {
>>> m_ptr->setSomething(); // ...but unfortunately, this is okay too=
=20
>>> :-(
>>> return 42;
>>> }
>>> };
>>>
>>> Now, if we are on the same wavelength, and I will assume in the=20
>>> following that this is the case, this is precisely the kind of issue th=
at=20
>>> propagate_const is designed to help with.
>>>
>>> Let us now turn our attention to copy operations. Ideally, polymorphic=
=20
>>> objects should be deep-copyable, just like any regular value type.=20
>>> Unfortunately, in another application of the zero cost abstraction=20
>>> principle, C++ provides no easy way to do this. Anyone who wants to com=
bine=20
>>> value semantics and dynamic polymorphism needs to provide explicit supp=
ort=20
>>> for deep copies across the entire class hierarchy, which is cumbersome =
to=20
>>> begin with and can get problematic when interacting with third-party=20
>>> libraries.
>>>
>>
>> Arthur has addressed your concerns with the same points I would have=20
>> raised (thanks Arthur). On the related topic of deep-copies and=20
>> const-propagation we're working on polymorphic_value which aims to tackl=
e=20
>> copies through class heirarchies. I'd be interested if you have a use ca=
se=20
>> where one of propagate_const and polymorphic_value does not solve your=
=20
>> problem.=20
>>
>>
>> https://github.com/jbcoe/polymorphic_value/blob/master/talks/2017_1_25_c=
xx_london.md
>> https://github.com/jbcoe/polymorphic_value/blob/master/draft.md
>>
>
> I love the idea of polymorphic_value. I think that once available, it=20
> should be taught to new C++ programmers as the standard way to manipulate=
=20
> polymorphic objects, and that anyone should strongly consider using it fo=
r=20
> this purpose in new codebases, or whenever a mature codebase gets=20
> completely rewritten.
>
> One thing which I do not think polymorphic_value can do, however, is be=
=20
> gradually introduced in an existing codebase. By design, polymorphic_valu=
e=20
> has to be constructed from pointer-to-derived,=20
>
>
> Please construct it from a value, using the factory method or roll your=
=20
> own factory method. The pointer constructor is a 'sharp knife'.
>
>
> and is thus fundamentally incompatible with the massive body of existing=
=20
> C++ code that uses pointer-to-base for polymorphism.=20
>
>
> Can you elaborate here? Maybe an example would be helpful.
>
I am working on a codebase that was mostly written in the late 90s and=20
early 2000s, back when object-oriented programming was going to save the=20
world and you couldn't write a single line of code without putting it=20
behind a virtual interface. Overall, it's a couple millions of lines of=20
code, written by a couple hundred people, many of which lacked training in=
=20
basic software engineering and have since left the project. So you know,=20
business as usual: let's focus on one tiny bit after another, and improve=
=20
things one baby step at a time, while doing our best to keep that monster=
=20
alive.
As an extra challenge, we're adding threads to this thing, which was=20
obviously never designed for that. Historically, "we'll just run one=20
sequential process per CPU core" bought us a lot of time, but ultimately we=
=20
needed to stop as RAM/core went down and RAM usage went up. I have thus=20
decided to focus a bit on the issue of const-correctness, in an attempt to=
=20
contain the areas that can do damage in a multithreaded environment.
One of the tentacles of this octopus that I'm working on is a scientific=20
data analysis package that starts from a 3D point cloud and attempts to=20
extract helicoidal features from it, in a fashion that is tolerant to both=
=20
false positives and negatives in the input dataset. The point cloud is=20
located on a fairly complex model of the detector, which in software is=20
fully implemented using class hierarchies. You have a class hierarchy for=
=20
volumes, a class hierarchy for materials, a class hierarchy for surfaces, a=
=20
class hierarchy for layers (mostly a group of volumes with a bounding box),=
=20
and so on, and all these classes cross-reference one another using=20
pointer-to-base-class, sometimes raw (e.g. Surface*), sometimes owning=20
(e.g. shared_ptr<Surface>).
If I take the Surface class hierarchy as an example, a quick search for=20
"Surface*", "shared_ptr<Surface>" and "shared_ptr<const Surface>", where=20
Surface is the base class of all concrete surface classes, leads a couple=
=20
of hundreds of results. Several of these reside in public interfaces, both=
=20
as inputs and as outputs, and changing them could have broader implication=
=20
for clients that I'm not aware of. Outputs are not a problem: I can just=20
take a pointer to the contents of polymorphic value. But inputs, as far as=
=20
I can tell, are a much bigger issue: if a client ever gives us a Surface*,=
=20
well, polymorphic_value will not be able to help us.
This is just one example of a single class hierarchy, in a single package=
=20
of a big codebase. And it's far from the worst package that we have, people=
=20
have already spent quite a lot of time cleaning it up. But it does not make=
=20
me very optimistic about the viability of migrating to polymorphic_value in=
=20
this kind of situation.
=20
> Once you introduce a polymorphic_value somewhere, you also need to rewrit=
e=20
> every other piece of client code that uses the same class hierarchy, ofte=
n=20
> including third-party libraries that you may have no easy access to. Even=
=20
> though the final code will be cleaner, the process will be even more cost=
ly=20
> as adding clone() methods everywhere. And for large projects, that will=
=20
> just not be seen as a viable endeavour.
>
>
> I've not found this to be the case though, like many things, it probably=
=20
> depends on patterns at use in the code.
>
> I believe that with the modifications that I am proposing, propagate_cons=
t=20
> would be a better fit for such refactoring purposes. With it, one could=
=20
> progressively introduce const-correct pointer manipulation at relatively=
=20
> little cost. Sure, you wouldn't have deep copies, but hey, developers of=
=20
> existing codebases never had them to begin with. So we aren't going to mi=
ss=20
> them in a context of progressive code evolution. And if error-prone shall=
ow=20
> copies are harder to perform by accident, well, all the better.
>
> =20
>
>
>>> class Interface
>>> {
>>> public:
>>> // This kind of boilerplate must be added to every class supporting=
=20
>>> deep copy...
>>> virtual std::unique_ptr<Interface> clone() const =3D 0;
>>> };
>>>
>>> class Implementation : public Interface
>>> {
>>> public:
>>> // ...and replicated again and again in every single non-abstract=20
>>> child. No fun.
>>> std::unique_ptr<Interface> clone() const final override
>>> {
>>> return std::make_unique<Implementation>( *this );
>>> }
>>> };
>>>
>>> But thankfully, sometimes, we can do without a true deep copy, and=20
>>> satisfy ourselves with a shallow copy. And in fact, for some use cases,=
a=20
>>> shallow copy will even be exactly what we want:
>>>
>>> - When building some variety of search acceleration structure=20
>>> (hashmap, neighbour locator, axis-aligned-bounding box...)
>>> - When many "slave objects" share a reference to some common,=20
>>> potentially large "master object"
>>>
>>> Unfortunately, even though all standard C++ pointer types make it=20
>>> trivial to shallow-copy a polymorphic object, propagate_const gets in o=
ur=20
>>> way here by being move only. I see this design choice as a shortcoming,=
=20
>>> because it reduces the usefulness of propagate_const in many dynamic=20
>>> polymorphism use cases where the underlying pointer type would have don=
e=20
>>> just fine. For example, without shallow copy operations...
>>>
>>> - You cannot let the compiler implement a shallow copy constructor=
=20
>>> for you using "=3D default"
>>> - You cannot use std::copy_if to find objects matching some=20
>>> predicate in an internal dataset
>>> - You cannot easily build object search acceleration structures,=20
>>> such as hashmaps or AABBs
>>> - You cannot easily share a reference to an object wrapped by=20
>>> propagate_const with other objects
>>>
>>> I am aware that shallow copies are possible using the get() and=20
>>> get_underlying() operations. I do not see this as a satisfactory answer=
,=20
>>> because it breaks every STL-ish algorithm that expects a copy construct=
or.=20
>>> Of course, I could build a propagate_const wrapper that has a copy=20
>>> constructor myself, and this is what I would end up doing if=20
>>> propagate_const were accepted in the STL in its current form. But I thi=
nk=20
>>> that the use cases that I mentioned above are valid enough to warrant=
=20
>>> changing the design of propagate_const instead.
>>>
>> I am aware that in order to be const-correct, a shallow copy constructor=
=20
>>> for propagate_const would need to operate from a non-const reference. I=
am=20
>>> fine with this tradeoff. The main reason we usually allow ourselves to =
make=20
>>> mutable copies from const objects is because we assume the copy to be=
=20
>>> independent from the original object. This assumption is broken when ma=
king=20
>>> shallow copies through pointer or reference types. Sure, many APIs will=
be=20
>>> broken initially, but that is unavoidable when fixing old programming=
=20
>>> language flaws. Bugs will be reported, and interfaces will be fixed, th=
e=20
>>> way it's always been done.
>>>
>>>
>>> For prior art, if you look at the STL's documentation, you will find=20
>>> that customizable algorithms that copy const data, such as std::copy_if=
,=20
>>> explicitly do NOT require the user-provided functions to consume the da=
ta=20
>>> by const-reference. So the idea of copying from non-const is not new. T=
he=20
>>> C++ community only needs to re-discover it.
>>>
>>>
>>>
>> We considered making copies from non-const propagate_const legal but wer=
e=20
>> strongly dissuaded by 3 major standard library implementers.
>>
>
> Sure, they disagreed. But what were their argument for it? Was it just a=
=20
> case of "the maffia said no"?
>
> =20
>
>> =20
>>
>>> I am also aware of the objection, made in a thread linked above, that=
=20
>>> "We were not confident that a non-const copy constructor would protect =
the=20
>>> user from accidental loss of const and accidental mutable shared state"=
.. As=20
>>> far as I'm concerned, this hand-waving statement was not properly=20
>>> justified. From my perspective, the proposed alternative of using=20
>>> get_underlying seems to be much bigger breach of const-correctness than=
a=20
>>> copy constructor that operates from mutable, because from a semantic po=
int=20
>>> of view, get_underlying is essentially a silent const_cast that standar=
d=20
>>> code analysis tools won't catch.
>>>
>>>
>>>
>> We added `get_underlying` as an nicer alternative to `reinterpret_cast`,=
=20
>> which would give access to the underlying pointer.
>>
>> Our design sought to avoid silent surprise. Granted a user can do the=20
>> wrong thing with get_underlying but the code is written by the user, not=
by=20
>> the compiler. Forcing the user to be explicit offers some protection.
>>
>
> Been there, done that. When you see all the tools that C++ and its=20
> standard library provide to help you shoot yourself in the foot, it is=20
> always very tempting to roll your own, thinking that surely it will do th=
e=20
> job better. Except usually, it doesn't. When what you want is a cast, mak=
e=20
> it a cast, don't try to make it look nicer by burying it in custom=20
> abstractions.
>
> Let's take a step back and think about this from an API design point of=
=20
> view. Why makes get_underlying such an poor interface?
>
> 1/ By returning a reference to the internal pointer object, you leak=20
> implementation details
> 2/ By leaking implementation details, you lose the ability to maintain th=
e=20
> single most important class invariant of propagate_const, which is that=
=20
> type-safe accesses to a const propagate_const wrapper should not give one=
=20
> write access to the internal object.
>
> Now, I am aware that we are discussing C++ API design here. Leaking=20
> implementation details and violating class invariants is okay, as long as=
=20
> it can give me that 0.1% performance improvement in some very obscure use=
=20
> case that someone thought about someday. One a single compiler. With the=
=20
> right combination of flags. Until the next release. Perhaps.
>
> But in the case of get_underlying, you could actually leak these=20
> implementation details, if you really wanted to, without violating your=
=20
> class invariants along the way. The fix is simple, really. Make=20
> get_underlying a non-const method of the propagate_const object, and drop=
=20
> get_underlying from const. Be serious about interface guarantees.
>
> If your users come whining because they can't easily extract a=20
> pointer-to-mutable from a const object, try to give them the talk about=
=20
> const correctness. If they won't listen, tell them to const_cast the=20
> propagate_const wrapper. But don't compromise your API just to make stupi=
d=20
> operations more convenient to write. It is never worth it.
>
>
> =20
>
>>
>>> Const-correct convertibility to the underlying pointer type
>>>
>>> Sometimes, one has to extract a pointer back from the propagate_const=
=20
>>> wrapper. I would see two main use cases for this:
>>>
>>> - To feed a legacy API that is not (yet?) compatible with=20
>>> propagate_const.
>>> - To turn a propagate_const<T> into a pointer-to-const, when the=20
>>> recipient should not be able to modify to the target object
>>>
>>> The current propagate_const interface provides three ways to do this:
>>>
>>> - Call get() and get a raw pointer
>>> - Rely on an implicit cast to raw pointer, if available
>>> - Use get_underlying to access the internals of propagate_const.
>>>
>>> As I'm going to elaborate, the first two operations are not always the=
=20
>>> right tool for the job at hand, because they fail to convey important=
=20
>>> ownership information from the underlying pointer type. Whereas=20
>>> get_underlying, as currently implemented, is a flawed interface that go=
es=20
>>> directly against the design goals of propagate_const and should be=20
>>> eradicated before standardization.
>>>
>>>
>>> As a use case, suppose that as discussed previously, I am working a=20
>>> "slave object", which holds a shared_ptr to an associated "master objec=
t"=20
>>> that it shares with other slaves. To improve the const-correctness of t=
his=20
>>> design, I have decided to refactor the shared_ptr into a=20
>>> propagate_const<shared_ptr>. So far, so good.
>>>
>>>
>>> class Master
>>> {
>>> ...
>>> };
>>>
>>> class Slave
>>> {
>>> private:
>>> // This was refactored from "std::shared_ptr<Master> m_master"
>>> std::propagate_const<std::shared_ptr<Master>> m_master;
>>>
>>> public:
>>> Slave( std::shared_ptr<Master> master )
>>> : m_master(master)
>>> {
>>> m_master->addSlave( *this );
>>> }
>>>
>>> ...
>>> };=20
>>>
>> But as I proceed with the refactoring, I discover that the Slave class=
=20
>>> used to provide a method that shares access to its master:
>>>
>>> std::shared_ptr<Master> Slave::getMaster() const
>>> {
>>> return m_master;
>>> }
>>>
>>> This does not compile anymore. And I'm happy about that: it should neve=
r=20
>>> have compiled to begin with. Returning non-const access to my members f=
rom=20
>>> a const method definitely does not match my idea of const-correctness!
>>>
>>> Instead, I would like to only provide const access to the master object=
,=20
>>> like so:
>>>
>>> std::shared_ptr<const Master> Slave::getMaster() const;
>>>
>>> If my clients are well-behaved and do not mutate the master object, thi=
s=20
>>> will be a minimally invasive interface change. It will require only min=
or=20
>>> client rewrites, the kind that could be automated by sed or an IDE. It=
=20
>>> could even require no client rewrite at all if I end up being lucky and=
=20
>>> have clients that were using auto and friends.
>>>
>>> Unfortunately, the current propagate_const interface does not allow me=
=20
>>> to implement this method in a clean way.
>>>
>>>
>> Sadly that's intended. We can't guarantee that "clients are well-behaved=
=20
>> and do not mutate the master object".
>>
>
> And you do not need to, that's my job. You give me a well-designed=20
> const-correct pointer wrapper, and I'll use it to improve the=20
> const-correctness of the codebase that I'm responsible of. If I find brok=
en=20
> client code, I will do my best to fix it myself, and send an angry e-mail=
=20
> to the person responsible if I don't manage. It's called maintenance.
>
> But when I'm going through the pain of doing this, I would like to do so=
=20
> using quality tools that give me the impression that the effort is=20
> worthwhile, and won't be undermined on the day after by someone trying to=
=20
> do something stupid, going through the STL documentation and thinking "oh=
,=20
> I know, I'll just use get_underlying...".
>
> When people start to break the thread-safety of our code by introducing=
=20
> mutation in places where it doesn't belong, I want them to explicitly wri=
te=20
> down the dreaded word "cast" and feel that chill going down their spine,=
=20
> telling them that they're doing something wrong. I want them to figure ou=
t=20
> that the basic design of their code is broken, and to understand that the=
y=20
> need to fix it before submitting it upstream. I don't want the only line =
of=20
> defense against software chaos to be me telling them at the end, when the=
y=20
> finally submit their merge request, that I can't accept it into our=20
> codebase and they need to rewrite half of it.
>
> Good interfaces save everyone's time by making bad things look bad. Let's=
=20
> have more of them in C++.
>
> =20
>
>> =20
>>
>>> The obvious code snippet would not work due to the lack of an=20
>>> appropriate implicit conversion:
>>>
>>> std::shared_ptr<const Master> Slave::getMaster() const
>>> {
>>> return m_master; // ERROR: No implicit conversion from=20
>>> propagate_const<shared_ptr>!
>>> }
>>>
>>> Using get() here would be the perfect way to introduce use after free=
=20
>>> bugs:
>>>
>>> std::shared_ptr<const Master> Slave::getMaster() const
>>> {
>>> const Master* master =3D m_master.get();
>>> return std::shared_ptr<Master>( master ); // INCORRECT: Two owners=
=20
>>> for one object!
>>> }
>>>
>>> And using get_underlying will both break const correctness and fail to=
=20
>>> provide me with the correct return type:
>>>
>>> std::shared_ptr<const Master> Slave::getMaster() const
>>> {
>>> auto master =3D std::get_underlying( m_master ); // This is an=20
>>> std::shared_ptr<Master>
>>> return master; // ERROR: No implicit conversion to=20
>>> std::shared_ptr<const Master>!
>>> }
>>>
>>> To implement this method, in addition to the const-incorrect=20
>>> get_underlying interface, I would also need a const cast!
>>>
>>> std::shared_ptr<const Master> Slave::getMaster() const
>>> {
>>> auto master =3D std::get_underlying( m_master );=20
>>> return std::const_pointer_cast<const Master>( master );
>>> }
>>>
>>>
>>> For sure, I would never want an abomination like this to pass code=20
>>> review :)
>>>
>>>
>>> Agreed.
>> =20
>>
>>> This little thought experiment showcases two major issues with=20
>>> get_underlying:
>>>
>>> - Counter to the goal of of propagate_const, it makes it trivial to=
=20
>>> violate const-correctness without the compiler noticing (or, for tha=
t=20
>>> matter, any developer or static analysis tool that is unfamiliar wit=
h the=20
>>> propagate_const API).
>>> - By not returning the const-correct type, get_underlying can make=
=20
>>> it unnecessarily hard to implement const-correct interfaces.
>>> =20
>>> The authors of the original propagate_const proposal were aware that th=
e=20
>>> get_underlying interface was suboptimal, and for this reason they opted=
to=20
>>> make it less usable by hiding it as a free function instead of making i=
t a=20
>>> proper method of propagate_const. But personnally, I would go further: =
drop=20
>>> get_underlying altogether, and replace it with something that is both=
=20
>>> const-correct and respectful of pointer ownership issues.
>>>
>>>
>>> My counter-proposal would be to have a propagate_const method, maybe=20
>>> called "share()", which enables sharing access to the data pointed by=
=20
>>> propagate_const with due respect paid to both const-correctness and the=
=20
>>> underlying pointer's ownership semantics. In practice:
>>>
>>> - If called on propagate_const<T*>, share() should return a T*
>>> - If called on const propagate_const<T*>, share() should return a=20
>>> const T*
>>> - If called on propagate_const<shared_ptr<T>>, share() should return=
=20
>>> a shared_ptr<T>
>>> - If called on const propagate_const<shared_ptr<T>>, share() should=
=20
>>> return a shared_ptr<const T>
>>> - share() should not be defined on propagate_const<unique_ptr<T>>=20
>>> since that pointer does not support sharing ownership
>>>
>>> In addition, as a convenience, whenever a share() method exists, an=20
>>> implicit cast could be provided to the underlying pointer type as a way=
to=20
>>> make such ownership sharing more convenient and to enable things like=
=20
>>> comparison of containers of shared_ptr<T> with containers of=20
>>> propagate_const<shared_ptr<T>> through standard (STL-ish) algorithms.
>>>
>>>
>>> If you really, positively want something like get_underlying to exist,=
=20
>>> it should only be applicable to non-const object. This alone would make=
it=20
>>> const-correct, and thus remove the need to implement it as a free funct=
ion=20
>>> and be careful when using it.
>>>
>>>
>>> This is a pretty convincing argument.
> Perhaps a paper for Toronto?
> Let me know if you'd like any input.
>
Well, to begin with, I do not completely understand what you are looking=20
for. Would you like me to write some sort of LaTeX article describing the=
=20
proposals that I've outlined here in a more formal way?
=20
> With this simple change, shameless people who want to get their hands=20
>>> dirty and extract a mutable pointer from a const wrapper will then need=
to=20
>>> do some extra work, as should be expected of them in my opinion:
>>>
>>>
>>> using ConstPropagator =3D std::propagate_const<std::shared_ptr<T>>;
>>>
>>> const ConstPropagator& immutableRef =3D ... ;
>>> std::shared_ptr<T> mutableRef =3D const_cast<ConstPropagator*>( &immuta=
bleRef=20
>>> )->get_underlying();
>>>
>>> --=20
>>> You received this message because you are subscribed to the Google=20
>>> Groups "ISO C++ Standard - Future Proposals" group.
>>> To unsubscribe from this group and stop receiving emails from it, send=
=20
>>> an email to std-proposal...@isocpp.org.
>>> To post to this group, send email to std-pr...@isocpp.org.
>>> To view this discussion on the web visit=20
>>> https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/9ad3f22a-a=
619-4488-bf1e-303641c968b9%40isocpp.org=20
>>> <https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/9ad3f22a-=
a619-4488-bf1e-303641c968b9%40isocpp.org?utm_medium=3Demail&utm_source=3Dfo=
oter>
>>> .
>>>
>>
>> --=20
> You received this message because you are subscribed to the Google Groups=
=20
> "ISO C++ Standard - Future Proposals" group.
> To unsubscribe from this group and stop receiving emails from it, send an=
=20
> email to std-proposal...@isocpp.org <javascript:>.
> To post to this group, send email to std-pr...@isocpp.org <javascript:>.
> To view this discussion on the web visit=20
> https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/189d5497-36b=
b-4c9c-8992-13d7eaa36b6b%40isocpp.org=20
> <https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/189d5497-36=
bb-4c9c-8992-13d7eaa36b6b%40isocpp.org?utm_medium=3Demail&utm_source=3Dfoot=
er>
> .
>
>
--=20
You received this message because you are subscribed to the Google Groups "=
ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp=
..org/d/msgid/std-proposals/c0bef11f-1eba-48a2-b11d-8678207822ca%40isocpp.or=
g.
------=_Part_179_278325470.1488019280402
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr"><br><br>Le vendredi 24 f=C3=A9vrier 2017 17:32:29 UTC+1, J=
onathan Coe a =C3=A9crit=C2=A0:<blockquote class=3D"gmail_quote" style=3D"m=
argin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"=
><div dir=3D"auto"><div></div><div><br></div><div><br>On 24 Feb 2017, at 16=
:06, Hadrien Grasland <<a href=3D"javascript:" target=3D"_blank" gdf-obf=
uscated-mailto=3D"NhQZldkPAgAJ" rel=3D"nofollow" onmousedown=3D"this.href=
=3D'javascript:';return true;" onclick=3D"this.href=3D'javascri=
pt:';return true;">hadrien....@gmail.com</a>> wrote:<br><br></div><b=
lockquote type=3D"cite"><div><div dir=3D"ltr"><br><br>Le mercredi 22 f=C3=
=A9vrier 2017 09:56:10 UTC+1, Jonathan Coe a =C3=A9crit=C2=A0:<blockquote c=
lass=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-left:1px #c=
cc solid;padding-left:1ex"><div dir=3D"ltr"><div><br><div class=3D"gmail_qu=
ote">On 21 February 2017 at 14:54, Hadrien Grasland wrote:<br><blockquote c=
lass=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left-width:1p=
x;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1=
ex"><div dir=3D"ltr">Hi everyone,<br><br>I am not sure what this group'=
s policy is regarding resurrection of old threads, so I have decided to cre=
ate a new one. But I would like to second two comments that were previously=
made on the propagate_const proposal of N4617:<br><br>1/ That it should be=
possible to copy a propagate_const<T> wrapper ( <a href=3D"https://g=
roups.google.com/a/isocpp.org/forum/#!searchin/std-proposals/propagate_cons=
t/std-proposals/1uDKcA9bssU/AZ-aQNAZAAAJ" rel=3D"nofollow" target=3D"_blank=
" onmousedown=3D"this.href=3D'https://groups.google.com/a/isocpp.org/fo=
rum/#!searchin/std-proposals/propagate_const/std-proposals/1uDKcA9bssU/AZ-a=
QNAZAAAJ';return true;" onclick=3D"this.href=3D'https://groups.goog=
le.com/a/isocpp.org/forum/#!searchin/std-proposals/propagate_const/std-prop=
osals/1uDKcA9bssU/AZ-aQNAZAAAJ';return true;">https://groups.google.com=
/a/<wbr>isocpp.org/forum/#!searchin/<wbr>std-proposals/propagate_const/<wbr=
>std-proposals/1uDKcA9bssU/AZ-<wbr>aQNAZAAAJ</a> ).<br>2/ That propagate_co=
nst<T> should be convertible to a const-correct form of the underlyin=
g pointer type ( <a href=3D"https://groups.google.com/a/isocpp.org/forum/#!=
searchin/std-proposals/propagate_const/std-proposals/7rSMtvQVASk/niLSKtkcBw=
AJ" rel=3D"nofollow" target=3D"_blank" onmousedown=3D"this.href=3D'http=
s://groups.google.com/a/isocpp.org/forum/#!searchin/std-proposals/propagate=
_const/std-proposals/7rSMtvQVASk/niLSKtkcBwAJ';return true;" onclick=3D=
"this.href=3D'https://groups.google.com/a/isocpp.org/forum/#!searchin/s=
td-proposals/propagate_const/std-proposals/7rSMtvQVASk/niLSKtkcBwAJ';re=
turn true;">https://groups.google.com/a/<wbr>isocpp.org/forum/#!searchin/<w=
br>std-proposals/propagate_const/<wbr>std-proposals/7rSMtvQVASk/<wbr>niLSKt=
kcBwAJ</a> ).<br><br>Let me motivate, through some concrete use cases, why =
I think that both of these changes are necessary.<br><br><br><font size=3D"=
4">Need for shallow copy operations</font><br><br>There are many reasons to=
use pointers in C++. A very common use case, however, is dynamic polymorph=
ism:<br><ul><li>Different implementations of a virtual interface may have d=
ifferent sizes</li><li>C++, in its commendable quest for zero-cost abstract=
ion, does not provide native support for dynamically sized types</li><li>Wh=
en combined, these two language design choices lead to the conclusion that =
if the set of implementations of an interface is not known as compile time,=
polymorphism intrinsically requires pointer indirection in C++</li></ul>Fo=
r this use case, pointer constness semantics are inadequate. If you use a p=
ointer-to-const, you cannot modify the target object, which is often fine b=
ut tends to be excessively limitating for some use cases, particularly when=
implementing constructors and factory functions. If, on the other hand, yo=
u use a pointer-to-mutable, you open a const-correctness hole by allowing y=
ourself to mutate a const object without any compiler warning. Quite frankl=
y, none of these options are very appealing.<br><br><div style=3D"backgroun=
d-color:rgb(250,250,250);border:1px solid rgb(187,187,187)"><code><div><spa=
n style=3D"color:rgb(136,0,0)">// Pointer-to-const restricts some valid use=
cases of polymorphism</span><span style=3D"color:rgb(0,0,0)"><br></span><s=
pan style=3D"color:rgb(0,0,136)">class</span><span style=3D"color:rgb(0,0,0=
)"> </span><span style=3D"color:rgb(102,0,102)">FirstTry</span><span style=
=3D"color:rgb(0,0,0)"><br></span><span style=3D"color:rgb(102,102,0)">{</sp=
an><span style=3D"color:rgb(0,0,0)"></span><br><code><span style=3D"color:r=
gb(0,0,136)">private</span><span style=3D"color:rgb(102,102,0)">:</span><sp=
an style=3D"color:rgb(0,0,0)"><br>=C2=A0 std</span><span style=3D"color:rgb=
(102,102,0)">::</span><span style=3D"color:rgb(0,0,0)">unique_ptr</span><sp=
an style=3D"color:rgb(102,102,0)"><</span><span style=3D"color:rgb(0,0,1=
36)">const</span><span style=3D"color:rgb(0,0,0)"> </span><span style=3D"co=
lor:rgb(102,0,102)">Interface</span><span style=3D"color:rgb(102,102,0)">&g=
t;</span><span style=3D"color:rgb(0,0,0)"> m_ptr</span><span style=3D"color=
:rgb(102,102,0)">;</span><span style=3D"color:rgb(0,0,0)"><br><br></span></=
code><span style=3D"color:rgb(0,0,136)">public</span><span style=3D"color:r=
gb(102,102,0)">:</span><span style=3D"color:rgb(0,0,0)"><br>=C2=A0 </span><=
span style=3D"color:rgb(102,0,102)">FirstTry</span><span style=3D"color:rgb=
(102,102,0)">()</span><span style=3D"color:rgb(0,0,0)"><br>=C2=A0 =C2=A0 </=
span><span style=3D"color:rgb(102,102,0)">:</span><span style=3D"color:rgb(=
0,0,0)"> m_ptr</span><span style=3D"color:rgb(102,102,0)">{</span><span sty=
le=3D"color:rgb(0,0,0)"> std</span><span style=3D"color:rgb(102,102,0)">::<=
/span><span style=3D"color:rgb(0,0,0)">make_unique</span><span style=3D"col=
or:rgb(102,102,0)"><</span><span style=3D"color:rgb(102,0,102)">Implemen=
tatio<wbr>n</span><span style=3D"color:rgb(102,102,0)">>(</span><span st=
yle=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,102,0)">...</=
span><span style=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,=
102,0)">)</span><span style=3D"color:rgb(0,0,0)"> </span><span style=3D"col=
or:rgb(102,102,0)">}</span><span style=3D"color:rgb(0,0,0)"><br>=C2=A0 </sp=
an><span style=3D"color:rgb(102,102,0)">{</span><span style=3D"color:rgb(0,=
0,0)"><br>=C2=A0 =C2=A0 m_ptr</span><span style=3D"color:rgb(102,102,0)">-&=
gt;</span><span style=3D"color:rgb(0,0,0)">setParent</span><span style=3D"c=
olor:rgb(102,102,0)">(</span><span style=3D"color:rgb(0,0,0)"> </span><span=
style=3D"color:rgb(102,102,0)">*</span><span style=3D"color:rgb(0,0,136)">=
this</span><span style=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rg=
b(102,102,0)">);</span><span style=3D"color:rgb(0,0,0)"> =C2=A0</span><span=
style=3D"color:rgb(136,0,0)">// ERROR:</span><span style=3D"color:rgb(0,0,=
0)"> Can't mutate through a const pointer!<br>=C2=A0 </span><span style=
=3D"color:rgb(102,102,0)">}</span><span style=3D"color:rgb(0,0,0)"><br></sp=
an><span style=3D"color:rgb(102,102,0)">};</span><span style=3D"color:rgb(0=
,0,0)"><br><br><br></span><span style=3D"color:rgb(136,0,0)">// Pointer-to-=
mutable breaks const correctness of polymorphism</span><span style=3D"color=
:rgb(0,0,0)"><br></span><span style=3D"color:rgb(0,0,136)">class</span><spa=
n style=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,0,102)">S=
econdTry</span><span style=3D"color:rgb(0,0,0)"><br></span><span style=3D"c=
olor:rgb(102,102,0)">{</span><span style=3D"color:rgb(0,0,0)"><br>private:<=
br>=C2=A0 std::unique_ptr<Interface> m_ptr;<br><br></span><span style=
=3D"color:rgb(0,0,136)">public</span><span style=3D"color:rgb(102,102,0)">:=
</span><span style=3D"color:rgb(0,0,0)"><br>=C2=A0 </span><span style=3D"co=
lor:rgb(102,0,102)">SecondTry</span><span style=3D"color:rgb(102,102,0)">()=
</span><span style=3D"color:rgb(0,0,0)"><br>=C2=A0 =C2=A0 </span><span styl=
e=3D"color:rgb(102,102,0)">:</span><span style=3D"color:rgb(0,0,0)"> m_ptr<=
/span><span style=3D"color:rgb(102,102,0)">{</span><span style=3D"color:rgb=
(0,0,0)"> std</span><span style=3D"color:rgb(102,102,0)">::</span><span sty=
le=3D"color:rgb(0,0,0)">make_unique</span><span style=3D"color:rgb(102,102,=
0)"><</span><span style=3D"color:rgb(102,0,102)">Implementatio<wbr>n</sp=
an><span style=3D"color:rgb(102,102,0)">>(</span><span style=3D"color:rg=
b(0,0,0)"> </span><span style=3D"color:rgb(102,102,0)">...</span><span styl=
e=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,102,0)">)</span=
><span style=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,102,=
0)">}</span><span style=3D"color:rgb(0,0,0)"><br>=C2=A0 </span><span style=
=3D"color:rgb(102,102,0)">{</span><span style=3D"color:rgb(0,0,0)"><br>=C2=
=A0 =C2=A0 m_ptr</span><span style=3D"color:rgb(102,102,0)">-></span><sp=
an style=3D"color:rgb(0,0,0)">setParent</span><span style=3D"color:rgb(102,=
102,0)">(</span><span style=3D"color:rgb(0,0,0)"> </span><span style=3D"col=
or:rgb(102,102,0)">*</span><span style=3D"color:rgb(0,0,136)">this</span><s=
pan style=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,102,0)"=
>);</span><span style=3D"color:rgb(0,0,0)"> =C2=A0</span><span style=3D"col=
or:rgb(136,0,0)">// This is now okay...</span><span style=3D"color:rgb(0,0,=
0)"><br>=C2=A0 </span><span style=3D"color:rgb(102,102,0)">}</span><span st=
yle=3D"color:rgb(0,0,0)"><br><br>=C2=A0 </span><span style=3D"color:rgb(0,0=
,136)">int</span><span style=3D"color:rgb(0,0,0)"> someRandomAccessor</span=
><span style=3D"color:rgb(102,102,0)">()</span><span style=3D"color:rgb(0,0=
,0)"> </span><span style=3D"color:rgb(0,0,136)">const</span><span style=3D"=
color:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,102,0)">{</span><spa=
n style=3D"color:rgb(0,0,0)"><br>=C2=A0 =C2=A0 m_ptr</span><span style=3D"c=
olor:rgb(102,102,0)">-></span><span style=3D"color:rgb(0,0,0)">setSometh=
ing</span><span style=3D"color:rgb(102,102,0)">();</span><span style=3D"col=
or:rgb(0,0,0)"> =C2=A0</span><span style=3D"color:rgb(136,0,0)">// ...but u=
nfortunately, this is okay too :-(</span><span style=3D"color:rgb(0,0,0)"><=
br>=C2=A0 =C2=A0 </span><span style=3D"color:rgb(0,0,136)">return</span><sp=
an style=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(0,102,102)">=
42</span><span style=3D"color:rgb(102,102,0)">;</span><span style=3D"color:=
rgb(0,0,0)"><br>=C2=A0 </span><span style=3D"color:rgb(102,102,0)">}</span>=
<span style=3D"color:rgb(0,0,0)"><br>};</span><span style=3D"color:rgb(0,0,=
0)"><br></span></div></code></div><br>Now, if we are on the same wavelength=
, and I will assume in the following that this is the case, this is precise=
ly the kind of issue that propagate_const is designed to help with.<br><br>=
Let us now turn our attention to copy operations. Ideally, polymorphic obje=
cts should be deep-copyable, just like any regular value type. Unfortunatel=
y, in another application of the zero cost abstraction principle, C++ provi=
des no easy way to do this. Anyone who wants to combine value semantics and=
dynamic polymorphism needs to provide explicit support for deep copies acr=
oss the entire class hierarchy, which is cumbersome to begin with and can g=
et problematic when interacting with third-party libraries.<br></div></bloc=
kquote><div><br></div><div>Arthur has addressed your concerns with the same=
points I would have raised (thanks Arthur). On the related topic of deep-c=
opies and const-propagation we're working on polymorphic_value which ai=
ms to tackle copies through class heirarchies. I'd be interested if you=
have a use case where one of propagate_const and polymorphic_value does no=
t solve your problem.=C2=A0</div><div><br></div><div><a href=3D"https://git=
hub.com/jbcoe/polymorphic_value/blob/master/talks/2017_1_25_cxx_london.md" =
rel=3D"nofollow" target=3D"_blank" onmousedown=3D"this.href=3D'https://=
www.google.com/url?q\x3dhttps%3A%2F%2Fgithub.com%2Fjbcoe%2Fpolymorphic_valu=
e%2Fblob%2Fmaster%2Ftalks%2F2017_1_25_cxx_london.md\x26sa\x3dD\x26sntz\x3d1=
\x26usg\x3dAFQjCNHiVpbjR-rhERETegsvD58f1-r4Hw';return true;" onclick=3D=
"this.href=3D'https://www.google.com/url?q\x3dhttps%3A%2F%2Fgithub.com%=
2Fjbcoe%2Fpolymorphic_value%2Fblob%2Fmaster%2Ftalks%2F2017_1_25_cxx_london.=
md\x26sa\x3dD\x26sntz\x3d1\x26usg\x3dAFQjCNHiVpbjR-rhERETegsvD58f1-r4Hw'=
;;return true;">https://github.com/jbcoe/<wbr>polymorphic_value/blob/master=
/<wbr>talks/2017_1_25_cxx_london.md</a><br></div><div><a href=3D"https://gi=
thub.com/jbcoe/polymorphic_value/blob/master/draft.md" rel=3D"nofollow" tar=
get=3D"_blank" onmousedown=3D"this.href=3D'https://www.google.com/url?q=
\x3dhttps%3A%2F%2Fgithub.com%2Fjbcoe%2Fpolymorphic_value%2Fblob%2Fmaster%2F=
draft.md\x26sa\x3dD\x26sntz\x3d1\x26usg\x3dAFQjCNE-iatIgiyMpvR4yv-KdAxgAdZu=
hQ';return true;" onclick=3D"this.href=3D'https://www.google.com/ur=
l?q\x3dhttps%3A%2F%2Fgithub.com%2Fjbcoe%2Fpolymorphic_value%2Fblob%2Fmaster=
%2Fdraft.md\x26sa\x3dD\x26sntz\x3d1\x26usg\x3dAFQjCNE-iatIgiyMpvR4yv-KdAxgA=
dZuhQ';return true;">https://github.com/jbcoe/<wbr>polymorphic_value/bl=
ob/master/<wbr>draft.md</a></div></div></div></div></blockquote><div><br>I =
love the idea of polymorphic_value. I think that once available, it should =
be taught to new C++ programmers as the standard way to manipulate polymorp=
hic objects, and that anyone should strongly consider using it for this pur=
pose in new codebases, or whenever a mature codebase gets completely rewrit=
ten.<br><br>One thing which I do not think polymorphic_value can do, howeve=
r, is be gradually introduced in an existing codebase. By design, polymorph=
ic_value has to be constructed from pointer-to-derived, </div></div></div><=
/blockquote><div><br></div><div>Please construct it from a value, using the=
factory method or roll your own factory method. The pointer constructor is=
a 'sharp knife'.</div><div><br></div><br><blockquote type=3D"cite"=
><div><div dir=3D"ltr"><div>and is thus fundamentally incompatible with the=
massive body of existing C++ code that uses pointer-to-base for polymorphi=
sm. </div></div></div></blockquote><div><br></div><div>Can you elaborate he=
re? Maybe an example would be helpful.</div></div></blockquote><div><br>I a=
m working on a codebase that was mostly written in the late 90s and early 2=
000s, back when object-oriented programming was going to save the world and=
you couldn't write a single line of code without putting it behind a v=
irtual interface. Overall, it's a couple millions of lines of code, wri=
tten by a couple hundred people, many of which lacked training in basic sof=
tware engineering and have since left the project. So you know, business as=
usual: let's focus on one tiny bit after another, and improve things o=
ne baby step at a time, while doing our best to keep that monster alive.<br=
><br>As an extra challenge, we're adding threads to this thing, which w=
as obviously never designed for that. Historically, "we'll just ru=
n one sequential process per CPU core" bought us a lot of time, but ul=
timately we needed to stop as RAM/core went down and RAM usage went up. I h=
ave thus decided to focus a bit on the issue of const-correctness, in an at=
tempt to contain the areas that can do damage in a multithreaded environmen=
t.<br><br>One of the tentacles of this octopus that I'm working on is a=
scientific data analysis package that starts from a 3D point cloud and att=
empts to extract helicoidal features from it, in a fashion that is tolerant=
to both false positives and negatives in the input dataset. The point clou=
d is located on a fairly complex model of the detector, which in software i=
s fully implemented using class hierarchies. You have a class hierarchy for=
volumes, a class hierarchy for materials, a class hierarchy for surfaces, =
a class hierarchy for layers (mostly a group of volumes with a bounding box=
), and so on, and all these classes cross-reference one another using point=
er-to-base-class, sometimes raw (e.g. Surface*), sometimes owning (e.g. sha=
red_ptr<Surface>).<br><br>If I take the Surface class hierarchy as an=
example, a quick search for "Surface*", "shared_ptr<Surf=
ace>" and "shared_ptr<const Surface>", where Surfac=
e is the base class of all concrete surface classes, leads a couple of hund=
reds of results. Several of these reside in public interfaces, both as inpu=
ts and as outputs, and changing them could have broader implication for cli=
ents that I'm not aware of. Outputs are not a problem: I can just take =
a pointer to the contents of polymorphic value. But inputs, as far as I can=
tell, are a much bigger issue: if a client ever gives us a Surface*, well,=
polymorphic_value will not be able to help us.<br><br>This is just one exa=
mple of a single class hierarchy, in a single package of a big codebase. An=
d it's far from the worst package that we have, people have already spe=
nt quite a lot of time cleaning it up. But it does not make me very optimis=
tic about the viability of migrating to polymorphic_value in this kind of s=
ituation.<br><br>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"mar=
gin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><=
div dir=3D"auto"><blockquote type=3D"cite"><div><div dir=3D"ltr"><div>Once =
you introduce a polymorphic_value somewhere, you also need to rewrite every=
other piece of client code that uses the same class hierarchy, often inclu=
ding third-party libraries that you may have no easy access to. Even though=
the final code will be cleaner, the process will be even more costly as ad=
ding clone() methods everywhere. And for large projects, that will just not=
be seen as a viable endeavour.<br><br></div></div></div></blockquote><div>=
<br></div><div>I've not found this to be the case though, like many thi=
ngs, it probably depends on patterns at use in the code.</div><br><blockquo=
te type=3D"cite"><div><div dir=3D"ltr"><div>I believe that with the modific=
ations that I am proposing, propagate_const would be a better fit for such =
refactoring purposes. With it, one could progressively introduce const-corr=
ect pointer manipulation at relatively little cost. Sure, you wouldn't =
have deep copies, but hey, developers of existing codebases never had them =
to begin with. So we aren't going to miss them in a context of progress=
ive code evolution. And if error-prone shallow copies are harder to perform=
by accident, well, all the better.<br><br>=C2=A0<br><br></div><blockquote =
class=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-left:1px #=
ccc solid;padding-left:1ex"><div dir=3D"ltr"><div><div class=3D"gmail_quote=
"><blockquote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;borde=
r-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid=
;padding-left:1ex"><div dir=3D"ltr"><br><div style=3D"background-color:rgb(=
250,250,250);border:1px solid rgb(187,187,187)"><code><div><span style=3D"c=
olor:rgb(0,0,136)">class</span><span style=3D"color:rgb(0,0,0)"> </span><sp=
an style=3D"color:rgb(102,0,102)">Interface</span><span style=3D"color:rgb(=
0,0,0)"><br></span><span style=3D"color:rgb(102,102,0)">{</span><span style=
=3D"color:rgb(0,0,0)"><br></span><span style=3D"color:rgb(0,0,136)">public<=
/span><span style=3D"color:rgb(102,102,0)">:</span><span style=3D"color:rgb=
(0,0,0)"><br>=C2=A0 </span><span style=3D"color:rgb(136,0,0)">// This kind =
of boilerplate must be added to every class supporting deep copy...</span><=
span style=3D"color:rgb(0,0,0)"><br>=C2=A0 </span><span style=3D"color:rgb(=
0,0,136)">virtual</span><span style=3D"color:rgb(0,0,0)"> std</span><span s=
tyle=3D"color:rgb(102,102,0)">::</span><span style=3D"color:rgb(0,0,0)">uni=
que_ptr</span><span style=3D"color:rgb(102,102,0)"><</span><span style=
=3D"color:rgb(102,0,102)">Interface</span><span style=3D"color:rgb(102,102,=
0)">></span><span style=3D"color:rgb(0,0,0)"> clone</span><span style=3D=
"color:rgb(102,102,0)">()</span><span style=3D"color:rgb(0,0,0)"> </span><s=
pan style=3D"color:rgb(0,0,136)">const</span><span style=3D"color:rgb(0,0,0=
)"> </span><span style=3D"color:rgb(102,102,0)">=3D</span><span style=3D"co=
lor:rgb(0,0,0)"> </span><span style=3D"color:rgb(0,102,102)">0</span><span =
style=3D"color:rgb(102,102,0)">;</span><span style=3D"color:rgb(0,0,0)"><br=
></span><span style=3D"color:rgb(102,102,0)">};</span><span style=3D"color:=
rgb(0,0,0)"><br><br></span><span style=3D"color:rgb(0,0,136)">class</span><=
span style=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,0,102)=
">Implementation</span><span style=3D"color:rgb(0,0,0)"> </span><span style=
=3D"color:rgb(102,102,0)">:</span><span style=3D"color:rgb(0,0,0)"> </span>=
<span style=3D"color:rgb(0,0,136)">public</span><span style=3D"color:rgb(0,=
0,0)"> </span><span style=3D"color:rgb(102,0,102)">Interface</span><span st=
yle=3D"color:rgb(0,0,0)"><br></span><span style=3D"color:rgb(102,102,0)">{<=
/span><span style=3D"color:rgb(0,0,0)"><br></span><span style=3D"color:rgb(=
0,0,136)">public</span><span style=3D"color:rgb(102,102,0)">:</span><span s=
tyle=3D"color:rgb(0,0,0)"><br>=C2=A0 </span><span style=3D"color:rgb(136,0,=
0)">// ...and replicated again and again in every single non-abstract child=
.. No fun.</span><span style=3D"color:rgb(0,0,0)"><br>=C2=A0 std</span><span=
style=3D"color:rgb(102,102,0)">::</span><span style=3D"color:rgb(0,0,0)">u=
nique_ptr</span><span style=3D"color:rgb(102,102,0)"><</span><span style=
=3D"color:rgb(102,0,102)">Interface</span><span style=3D"color:rgb(102,102,=
0)">></span><span style=3D"color:rgb(0,0,0)"> clone</span><span style=3D=
"color:rgb(102,102,0)">()</span><span style=3D"color:rgb(0,0,0)"> </span><s=
pan style=3D"color:rgb(0,0,136)">const</span><span style=3D"color:rgb(0,0,0=
)"> </span><span style=3D"color:rgb(0,0,136)">final</span><span style=3D"co=
lor:rgb(0,0,0)"> </span><span style=3D"color:rgb(0,0,136)">override</span><=
span style=3D"color:rgb(0,0,0)"><br>=C2=A0 </span><span style=3D"color:rgb(=
102,102,0)">{</span><span style=3D"color:rgb(0,0,0)"><br>=C2=A0 =C2=A0 </sp=
an><span style=3D"color:rgb(0,0,136)">return</span><span style=3D"color:rgb=
(0,0,0)"> std</span><span style=3D"color:rgb(102,102,0)">::</span><span sty=
le=3D"color:rgb(0,0,0)">make_unique</span><span style=3D"color:rgb(102,102,=
0)"><</span><span style=3D"color:rgb(102,0,102)">Implementatio<wbr>n</sp=
an><span style=3D"color:rgb(102,102,0)">>(</span><span style=3D"color:rg=
b(0,0,0)"> </span><span style=3D"color:rgb(102,102,0)">*</span><span style=
=3D"color:rgb(0,0,136)">this</span><span style=3D"color:rgb(0,0,0)"> </span=
><span style=3D"color:rgb(102,102,0)">);</span><span style=3D"color:rgb(0,0=
,0)"><br>=C2=A0 </span><span style=3D"color:rgb(102,102,0)">}</span><span s=
tyle=3D"color:rgb(0,0,0)"><br></span><span style=3D"color:rgb(102,102,0)">}=
;</span></div></code></div><br>But thankfully, sometimes, we can do without=
a true deep copy, and satisfy ourselves with a shallow copy. And in fact, =
for some use cases, a shallow copy will even be exactly what we want:<br><u=
l><li>When building some variety of search acceleration structure (hashmap,=
neighbour locator, axis-aligned-bounding box...)</li><li>When many "s=
lave objects" share a reference to some common, potentially large &quo=
t;master object"</li></ul>Unfortunately, even though all standard C++ =
pointer types make it trivial to shallow-copy a polymorphic object, propaga=
te_const gets in our way here by being move only. I see this design choice =
as a shortcoming, because it reduces the usefulness of propagate_const in m=
any dynamic polymorphism use cases where the underlying pointer type would =
have done just fine. For example, without shallow copy operations...<br><ul=
><li>You cannot let the compiler implement a shallow copy constructor for y=
ou using "=3D default"<br></li><li>You cannot use std::copy_if to=
find objects matching some predicate in an internal dataset</li><li>You ca=
nnot easily build object search acceleration structures, such as hashmaps o=
r AABBs</li><li>You cannot easily share a reference to an object wrapped by=
propagate_const with other objects</li></ul><p>I am aware that shallow cop=
ies are possible using the get() and get_underlying() operations. I do not =
see this as a satisfactory answer, because it breaks every STL-ish algorith=
m that expects a copy constructor. Of course, I could build a propagate_con=
st wrapper that has a copy constructor myself, and this is what I would end=
up doing if propagate_const were accepted in the STL in its current form. =
But I think that the use cases that I mentioned above are valid enough to w=
arrant changing the design of propagate_const instead.</p></div></blockquot=
e><blockquote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;borde=
r-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid=
;padding-left:1ex"><div dir=3D"ltr"><p></p><p>I am aware that in order to b=
e const-correct, a shallow copy constructor for propagate_const would need =
to operate from a non-const reference. I am fine with this tradeoff. The ma=
in reason we usually allow ourselves to make mutable copies from const obje=
cts is because we assume the copy to be independent from the original objec=
t. This assumption is broken when making shallow copies through pointer or =
reference types. Sure, many APIs will be broken initially, but that is unav=
oidable when fixing old programming language flaws. Bugs will be reported, =
and interfaces will be fixed, the way it's always been done.<br></p><p>=
<br></p><p>For prior art, if you look at the STL's documentation, you w=
ill find that customizable algorithms that copy const data, such as std::co=
py_if, explicitly do NOT require the user-provided functions to consume the=
data by const-reference. So the idea of copying from non-const is not new.=
The C++ community only needs to re-discover it.<br></p><p><br></p></div></=
blockquote><div><br></div><div>We considered making copies from non-const p=
ropagate_const legal but were strongly dissuaded by 3 major standard librar=
y implementers.</div></div></div></div></blockquote><div><br>Sure, they dis=
agreed. But what were their argument for it? Was it just a case of "th=
e maffia said no"?<br><br>=C2=A0</div><blockquote class=3D"gmail_quote=
" style=3D"margin:0;margin-left:0.8ex;border-left:1px #ccc solid;padding-le=
ft:1ex"><div dir=3D"ltr"><div><div class=3D"gmail_quote"><div>=C2=A0=C2=A0<=
/div><blockquote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;bo=
rder-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:so=
lid;padding-left:1ex"><div dir=3D"ltr"><p></p><p>I am also aware of the obj=
ection, made in a thread linked above, that "We were not confident tha=
t a non-const copy constructor would protect=20
the user from accidental loss of const and accidental mutable shared=20
state". As far as I'm concerned, this hand-waving statement was no=
t properly justified. From my perspective, the proposed alternative of usin=
g get_underlying seems to be much bigger breach of const-correctness than a=
copy constructor that operates from mutable, because from a semantic point=
of view, get_underlying is essentially a silent const_cast that standard c=
ode analysis tools won't catch.</p><p><br></p></div></blockquote><div><=
br></div><div>We added `get_underlying` as an nicer alternative to `reinter=
pret_cast`, which would give access to the underlying pointer.<br></div><di=
v><br></div><div>Our design sought to avoid silent surprise. Granted a user=
can do the wrong thing with get_underlying but the code is written by the =
user, not by the compiler. Forcing the user to be explicit offers some prot=
ection.</div></div></div></div></blockquote><div><br>Been there, done that.=
When you see all the tools that C++ and its standard library provide to he=
lp you shoot yourself in the foot, it is always very tempting to roll your =
own, thinking that surely it will do the job better. Except usually, it doe=
sn't. When what you want is a cast, make it a cast, don't try to ma=
ke it look nicer by burying it in custom abstractions.<br><br>Let's tak=
e a step back and think about this from an API design point of view. Why ma=
kes get_underlying such an poor interface?<br><br>1/ By returning a referen=
ce to the internal pointer object, you leak implementation details<br>2/ By=
leaking implementation details, you lose the ability to maintain the singl=
e most important class invariant of propagate_const, which is that type-saf=
e accesses to a const propagate_const wrapper should not give one write acc=
ess to the internal object.<br><br>Now, I am aware that we are discussing C=
++ API design here. Leaking implementation details and violating class inva=
riants is okay, as long as it can give me that 0.1% performance improvement=
in some very obscure use case that someone thought about someday. One a si=
ngle compiler. With the right combination of flags. Until the next release.=
Perhaps.<br><br>But in the case of get_underlying, you could actually leak=
these implementation details, if you really wanted to, without violating y=
our class invariants along the way. The fix is simple, really. Make get_und=
erlying a non-const method of the propagate_const object, and drop get_unde=
rlying from const. Be serious about interface guarantees.<br><br>If your us=
ers come whining because they can't easily extract a pointer-to-mutable=
from a const object, try to give them the talk about const correctness. If=
they won't listen, tell them to const_cast the propagate_const wrapper=
.. But don't compromise your API just to make stupid operations more con=
venient to write. It is never worth it.<br><br><br>=C2=A0<br></div><blockqu=
ote class=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-left:1=
px #ccc solid;padding-left:1ex"><div dir=3D"ltr"><div><div class=3D"gmail_q=
uote"><blockquote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;b=
order-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:s=
olid;padding-left:1ex"><div dir=3D"ltr"><p></p><br><font size=3D"4">Const-c=
orrect convertibility to the underlying pointer type</font><br><br>Sometime=
s, one has to extract a pointer back from the propagate_const wrapper. I wo=
uld see two main use cases for this:<br><ul><li>To feed a legacy API that i=
s not (yet?) compatible with propagate_const.<br></li><li>To turn a propaga=
te_const<T> into a pointer-to-const, when the recipient should not be=
able to modify to the target object</li></ul><p>The current propagate_cons=
t interface provides three ways to do this:</p><ul><li>Call get() and get a=
raw pointer</li><li>Rely on an implicit cast to raw pointer, if available<=
/li><li>Use get_underlying to access the internals of propagate_const.</li>=
</ul><p>As I'm going to elaborate, the first two operations are not alw=
ays the right tool for the job at hand, because they fail to convey importa=
nt ownership information from the underlying pointer type. Whereas get_unde=
rlying, as currently implemented, is a flawed interface that goes directly =
against the design goals of propagate_const and should be eradicated before=
standardization.</p><p><br></p><p>As a use case, suppose that as discussed=
previously, I am working a "slave object", which holds a shared_=
ptr to an associated "master object" that it shares with other sl=
aves. To improve the const-correctness of this design, I have decided to re=
factor the shared_ptr into a propagate_const<shared_ptr>. So far, so =
good.<br></p><p><br></p><p></p><div style=3D"background-color:rgb(250,250,2=
50);border:1px solid rgb(187,187,187)"><code><div><span style=3D"color:rgb(=
0,0,136)">class</span><span style=3D"color:rgb(0,0,0)"> </span><span style=
=3D"color:rgb(102,0,102)">Master</span><span style=3D"color:rgb(0,0,0)"><br=
></span><span style=3D"color:rgb(102,102,0)">{</span><span style=3D"color:r=
gb(0,0,0)"><br>=C2=A0 </span><span style=3D"color:rgb(102,102,0)">...</span=
><span style=3D"color:rgb(0,0,0)"><br></span><span style=3D"color:rgb(102,1=
02,0)">};</span><span style=3D"color:rgb(0,0,0)"><br><br></span><span style=
=3D"color:rgb(0,0,136)">class</span><span style=3D"color:rgb(0,0,0)"> </spa=
n><span style=3D"color:rgb(102,0,102)">Slave</span><span style=3D"color:rgb=
(0,0,0)"><br></span><span style=3D"color:rgb(102,102,0)">{</span><span styl=
e=3D"color:rgb(0,0,0)"><br></span><span style=3D"color:rgb(0,0,136)">privat=
e</span><span style=3D"color:rgb(102,102,0)">:</span><span style=3D"color:r=
gb(0,0,0)"><br>=C2=A0 </span><span style=3D"color:rgb(136,0,0)">// This was=
refactored from "std::shared_ptr<Master> m_master"</span><=
span style=3D"color:rgb(0,0,0)"><br>=C2=A0 std</span><span style=3D"color:r=
gb(102,102,0)">::</span><span style=3D"color:rgb(0,0,0)">propagate_const</s=
pan><span style=3D"color:rgb(102,102,0)"><</span><span style=3D"color:rg=
b(0,0,0)">std</span><span style=3D"color:rgb(102,102,0)">::</span><span sty=
le=3D"color:rgb(0,0,0)">shar<wbr>ed_ptr</span><span style=3D"color:rgb(102,=
102,0)"><</span><span style=3D"color:rgb(102,0,102)">Master</span><span =
style=3D"color:rgb(102,102,0)">>></span><span style=3D"color:rgb(0,0,=
0)"> m_master</span><span style=3D"color:rgb(102,102,0)">;</span><span styl=
e=3D"color:rgb(0,0,0)"><br><br></span><span style=3D"color:rgb(0,0,136)">pu=
blic</span><span style=3D"color:rgb(102,102,0)">:</span><span style=3D"colo=
r:rgb(0,0,0)"><br>=C2=A0 </span><span style=3D"color:rgb(102,0,102)">Slave<=
/span><span style=3D"color:rgb(102,102,0)">(</span><span style=3D"color:rgb=
(0,0,0)"> std</span><span style=3D"color:rgb(102,102,0)">::</span><span sty=
le=3D"color:rgb(0,0,0)">shared_ptr</span><span style=3D"color:rgb(102,102,0=
)"><</span><span style=3D"color:rgb(102,0,102)">Master</span><span style=
=3D"color:rgb(102,102,0)">></span><span style=3D"color:rgb(0,0,0)"> mast=
er </span><span style=3D"color:rgb(102,102,0)">)</span><span style=3D"color=
:rgb(0,0,0)"><br>=C2=A0 =C2=A0 </span><span style=3D"color:rgb(102,102,0)">=
:</span><span style=3D"color:rgb(0,0,0)"> m_master</span><span style=3D"col=
or:rgb(102,102,0)">(</span><span style=3D"color:rgb(0,0,0)">master</span><s=
pan style=3D"color:rgb(102,102,0)">)</span><span style=3D"color:rgb(0,0,0)"=
><br>=C2=A0 </span><span style=3D"color:rgb(102,102,0)">{</span><span style=
=3D"color:rgb(0,0,0)"><br>=C2=A0 =C2=A0 m_master</span><span style=3D"color=
:rgb(102,102,0)">-></span><span style=3D"color:rgb(0,0,0)">addSlave</spa=
n><span style=3D"color:rgb(102,102,0)">(</span><span style=3D"color:rgb(0,0=
,0)"> </span><span style=3D"color:rgb(102,102,0)">*</span><span style=3D"co=
lor:rgb(0,0,136)">this</span><span style=3D"color:rgb(0,0,0)"> </span><span=
style=3D"color:rgb(102,102,0)">);</span><span style=3D"color:rgb(0,0,0)"><=
br>=C2=A0 </span><span style=3D"color:rgb(102,102,0)">}</span><span style=
=3D"color:rgb(0,0,0)"><br><br>=C2=A0 ...<br></span><span style=3D"color:rgb=
(102,102,0)">};</span><span style=3D"font-family:arial,sans-serif;backgroun=
d-color:rgb(255,255,255)">=C2=A0</span></div></code></div></div></blockquot=
e><blockquote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;borde=
r-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid=
;padding-left:1ex"><div dir=3D"ltr"><p></p>But as I proceed with the refact=
oring, I discover that the Slave class used to provide a method that shares=
access to its master:<br><br><div style=3D"background-color:rgb(250,250,25=
0);border:1px solid rgb(187,187,187)"><code><div><span style=3D"color:rgb(0=
,0,0)">std</span><span style=3D"color:rgb(102,102,0)">::</span><span style=
=3D"color:rgb(0,0,0)">shared_ptr</span><span style=3D"color:rgb(102,102,0)"=
><</span><span style=3D"color:rgb(102,0,102)">Master</span><span style=
=3D"color:rgb(102,102,0)">></span><span style=3D"color:rgb(0,0,0)"> </sp=
an><span style=3D"color:rgb(102,0,102)">Slave</span><span style=3D"color:rg=
b(102,102,0)">::</span><span style=3D"color:rgb(0,0,0)">getMaster</span><sp=
an style=3D"color:rgb(102,102,0)">()</span><span style=3D"color:rgb(0,0,0)"=
> </span><span style=3D"color:rgb(0,0,136)">const</span><span style=3D"colo=
r:rgb(0,0,0)"><br></span><span style=3D"color:rgb(102,102,0)">{</span><span=
style=3D"color:rgb(0,0,0)"><br>=C2=A0 </span><span style=3D"color:rgb(0,0,=
136)">return</span><span style=3D"color:rgb(0,0,0)"> m_master</span><span s=
tyle=3D"color:rgb(102,102,0)">;</span><span style=3D"color:rgb(0,0,0)"><br>=
</span><span style=3D"color:rgb(102,102,0)">}</span></div></code></div><br>=
This does not compile anymore. And I'm happy about that: it should neve=
r have compiled to begin with. Returning non-const access to my members fro=
m a const method definitely does not match my idea of const-correctness!<br=
><br>Instead, I would like to only provide const access to the master objec=
t, like so:<br><br><div style=3D"background-color:rgb(250,250,250);border:1=
px solid rgb(187,187,187)"><code><div><span style=3D"color:rgb(0,0,0)">std<=
/span><span style=3D"color:rgb(102,102,0)">::</span><span style=3D"color:rg=
b(0,0,0)">shared_ptr</span><span style=3D"color:rgb(102,102,0)"><</span>=
<span style=3D"color:rgb(0,0,136)">const</span><span style=3D"color:rgb(0,0=
,0)"> </span><span style=3D"color:rgb(102,0,102)">Master</span><span style=
=3D"color:rgb(102,102,0)">></span><span style=3D"color:rgb(0,0,0)"> </sp=
an><span style=3D"color:rgb(102,0,102)">Slave</span><span style=3D"color:rg=
b(102,102,0)">::</span><span style=3D"color:rgb(0,0,0)">getMaster</span><sp=
an style=3D"color:rgb(102,102,0)">()</span><span style=3D"color:rgb(0,0,0)"=
> </span><span style=3D"color:rgb(0,0,136)">const</span><span style=3D"colo=
r:rgb(102,102,0)">;</span></div></code></div><br>If my clients are well-beh=
aved and do not mutate the master object, this will be a minimally invasive=
interface change. It will require only minor client rewrites, the kind tha=
t could be automated by sed or an IDE. It could even require no client rewr=
ite at all if I end up being lucky and have clients that were using auto an=
d friends.<br><br>Unfortunately, the current propagate_const interface does=
not allow me to implement this method in a clean way.<br><br></div></block=
quote><div><br></div><div>Sadly that's intended. We can't guarantee=
that "clients are well-behaved and do not mutate the master object&qu=
ot;.</div></div></div></div></blockquote><div><br>And you do not need to, t=
hat's my job. You give me a well-designed const-correct pointer wrapper=
, and I'll use it to improve the const-correctness of the codebase that=
I'm responsible of. If I find broken client code, I will do my best to=
fix it myself, and send an angry e-mail to the person responsible if I don=
't manage. It's called maintenance.<br><br>But when I'm going t=
hrough the pain of doing this, I would like to do so using quality tools th=
at give me the impression that the effort is worthwhile, and won't be u=
ndermined on the day after by someone trying to do something stupid, going =
through the STL documentation and thinking "oh, I know, I'll just =
use get_underlying...".<br><br>When people start to break the thread-s=
afety of our code by introducing mutation in places where it doesn't be=
long, I want them to explicitly write down the dreaded word "cast"=
; and feel that chill going down their spine, telling them that they're=
doing something wrong. I want them to figure out that the basic design of =
their code is broken, and to understand that they need to fix it before sub=
mitting it upstream. I don't want the only line of defense against soft=
ware chaos to be me telling them at the end, when they finally submit their=
merge request, that I can't accept it into our codebase and they need =
to rewrite half of it.<br><br>Good interfaces save everyone's time by m=
aking bad things look bad. Let's have more of them in C++.<br><br>=C2=
=A0</div><blockquote class=3D"gmail_quote" style=3D"margin:0;margin-left:0.=
8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir=3D"ltr"><div><div=
class=3D"gmail_quote"><div>=C2=A0</div><blockquote class=3D"gmail_quote" s=
tyle=3D"margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rg=
b(204,204,204);border-left-style:solid;padding-left:1ex"><div dir=3D"ltr">T=
he obvious code snippet would not work due to the lack of an appropriate im=
plicit conversion:<br><br><div style=3D"background-color:rgb(250,250,250);b=
order:1px solid rgb(187,187,187)"><code><div><span style=3D"color:rgb(0,0,0=
)">std</span><span style=3D"color:rgb(102,102,0)">::</span><span style=3D"c=
olor:rgb(0,0,0)">shared_ptr</span><span style=3D"color:rgb(102,102,0)"><=
const </span><span style=3D"color:rgb(102,0,102)">Master</span><span style=
=3D"color:rgb(102,102,0)">></span><span style=3D"color:rgb(0,0,0)"> </sp=
an><span style=3D"color:rgb(102,0,102)">Slave</span><span style=3D"color:rg=
b(102,102,0)">::</span><span style=3D"color:rgb(0,0,0)">getMaster</span><sp=
an style=3D"color:rgb(102,102,0)">()</span><span style=3D"color:rgb(0,0,0)"=
> </span><span style=3D"color:rgb(0,0,136)">const</span><span style=3D"colo=
r:rgb(0,0,0)"><br></span><span style=3D"color:rgb(102,102,0)">{</span><span=
style=3D"color:rgb(0,0,0)"><br>=C2=A0 </span><span style=3D"color:rgb(0,0,=
136)">return</span><span style=3D"color:rgb(0,0,0)"> m_master</span><span s=
tyle=3D"color:rgb(102,102,0)">;</span><span style=3D"color:rgb(0,0,0)">=C2=
=A0 // ERROR: No implicit conversion from propagate_const<shared_ptr>=
!<br></span><span style=3D"color:rgb(102,102,0)">}</span></div></code></div=
><br>Using get() here would be the perfect way to introduce use after free =
bugs:<br><br><div style=3D"background-color:rgb(250,250,250);border:1px sol=
id rgb(187,187,187)"><code><div><span style=3D"color:rgb(0,0,0)">std</span>=
<span style=3D"color:rgb(102,102,0)">::</span><span style=3D"color:rgb(0,0,=
0)">shared_ptr</span><span style=3D"color:rgb(102,102,0)"><const </span>=
<span style=3D"color:rgb(102,0,102)">Master</span><span style=3D"color:rgb(=
102,102,0)">></span><span style=3D"color:rgb(0,0,0)"> </span><span style=
=3D"color:rgb(102,0,102)">Slave</span><span style=3D"color:rgb(102,102,0)">=
::</span><span style=3D"color:rgb(0,0,0)">getMaster</span><span style=3D"co=
lor:rgb(102,102,0)">()</span><span style=3D"color:rgb(0,0,0)"> </span><span=
style=3D"color:rgb(0,0,136)">const</span><span style=3D"color:rgb(0,0,0)">=
<br></span><span style=3D"color:rgb(102,102,0)">{</span><span style=3D"colo=
r:rgb(0,0,0)"><br>=C2=A0 const Master* master =3D m_master.get();<br>=C2=A0=
</span><span style=3D"color:rgb(0,0,136)">return</span><span style=3D"colo=
r:rgb(0,0,0)"> std::shared_ptr<Master>( master</span><span style=3D"c=
olor:rgb(102,102,0)"> );</span><span style=3D"color:rgb(0,0,0)">=C2=A0 // I=
NCORRECT: Two owners for one object!<br></span><span style=3D"color:rgb(102=
,102,0)">}</span></div></code></div><br>And using get_underlying will both =
break const correctness and fail to provide me with the correct return type=
:<br><br><div style=3D"background-color:rgb(250,250,250);border:1px solid r=
gb(187,187,187)"><code><div><span style=3D"color:rgb(0,0,0)">std</span><spa=
n style=3D"color:rgb(102,102,0)">::</span><span style=3D"color:rgb(0,0,0)">=
shared_ptr</span><span style=3D"color:rgb(102,102,0)"><const </span><spa=
n style=3D"color:rgb(102,0,102)">Master</span><span style=3D"color:rgb(102,=
102,0)">></span><span style=3D"color:rgb(0,0,0)"> </span><span style=3D"=
color:rgb(102,0,102)">Slave</span><span style=3D"color:rgb(102,102,0)">::</=
span><span style=3D"color:rgb(0,0,0)">getMaster</span><span style=3D"color:=
rgb(102,102,0)">()</span><span style=3D"color:rgb(0,0,0)"> </span><span sty=
le=3D"color:rgb(0,0,136)">const</span><span style=3D"color:rgb(0,0,0)"><br>=
</span><span style=3D"color:rgb(102,102,0)">{</span><span style=3D"color:rg=
b(0,0,0)"><br>=C2=A0 auto master =3D std::get_underlying( m_master );=C2=A0=
// This is an std::shared_ptr<Master><br>=C2=A0 </span><span style=
=3D"color:rgb(0,0,136)">return</span><span style=3D"color:rgb(0,0,0)"> mast=
er;=C2=A0 // ERROR: No implicit conversion to std::shared_ptr<const Mast=
er>!</span><span style=3D"color:rgb(0,0,0)"><br></span><span style=3D"co=
lor:rgb(102,102,0)">}</span></div></code></div><br>To implement this method=
, in addition to the const-incorrect get_underlying interface, I would also=
need a const cast!<br><br><div style=3D"background-color:rgb(250,250,250);=
border:1px solid rgb(187,187,187)"><code><div><span style=3D"color:rgb(0,0,=
0)">std</span><span style=3D"color:rgb(102,102,0)">::</span><span style=3D"=
color:rgb(0,0,0)">shared_ptr</span><span style=3D"color:rgb(102,102,0)"><=
;const </span><span style=3D"color:rgb(102,0,102)">Master</span><span style=
=3D"color:rgb(102,102,0)">></span><span style=3D"color:rgb(0,0,0)"> </sp=
an><span style=3D"color:rgb(102,0,102)">Slave</span><span style=3D"color:rg=
b(102,102,0)">::</span><span style=3D"color:rgb(0,0,0)">getMaster</span><sp=
an style=3D"color:rgb(102,102,0)">()</span><span style=3D"color:rgb(0,0,0)"=
> </span><span style=3D"color:rgb(0,0,136)">const</span><span style=3D"colo=
r:rgb(0,0,0)"><br></span><span style=3D"color:rgb(102,102,0)">{</span><span=
style=3D"color:rgb(0,0,0)"><br>=C2=A0 auto master =3D std::get_underlying(=
m_master ); <br>=C2=A0 </span><span style=3D"color:rgb(0,0,136)">return</s=
pan><span style=3D"color:rgb(0,0,0)"> std::const_pointer_cast<const Mast=
er>( master );</span><span style=3D"color:rgb(0,0,0)"><br></span><span s=
tyle=3D"color:rgb(102,102,0)">}</span></div></code></div><p><br></p><p>For =
sure, I would never want an abomination like this to pass code review :)</p=
><p><br></p></div></blockquote><div>Agreed.</div><div>=C2=A0</div><blockquo=
te class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left-widt=
h:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-le=
ft:1ex"><div dir=3D"ltr"><p></p><p>This little thought experiment showcases=
two major issues with get_underlying:</p><ul><li>Counter to the goal of of=
propagate_const, it makes it trivial to violate const-correctness without =
the compiler noticing (or, for that matter, any developer or static analysi=
s tool that is unfamiliar with the propagate_const API).<br></li><li>By not=
returning the const-correct type, get_underlying can make it unnecessarily=
hard to implement const-correct interfaces.<br></li></ul><p></p><p>The aut=
hors of the original propagate_const proposal were aware that the get_under=
lying interface was suboptimal, and for this reason they opted to make it l=
ess usable by hiding it as a free function instead of making it a proper me=
thod of propagate_const. But personnally, I would go further: drop get_unde=
rlying altogether, and replace it with something that is both const-correct=
and respectful of pointer ownership issues.<br></p><p><br></p><p>My counte=
r-proposal would be to have a propagate_const method, maybe called "sh=
are()", which enables sharing access to the data pointed by propagate_=
const with due respect paid to both const-correctness and the underlying po=
inter's ownership semantics. In practice:</p><ul><li>If called on propa=
gate_const<T*>, share() should return a T*</li><li>If called on const=
propagate_const<T*>, share() should return a const T*</li><li>If cal=
led on propagate_const<shared_ptr<T>><wbr>, share() should retu=
rn a shared_ptr<T></li><li>If called on const propagate_const<shar=
ed_ptr<T>><wbr>, share() should return a shared_ptr<const T>=
</li><li>share() should not be defined on propagate_const<unique_ptr<=
T>> since that pointer does not support sharing ownership</li></ul><p=
>In addition, as a convenience, whenever a share() method exists, an implic=
it cast could be provided to the underlying pointer type as a way to make s=
uch ownership sharing more convenient and to enable things like comparison =
of containers of shared_ptr<T> with containers of propagate_const<=
shared_ptr<T>> through standard (STL-ish) algorithms.</p><p><br></=
p><p>If you really, positively want something like get_underlying to exist,=
it should only be applicable to non-const object. This alone would make it=
const-correct, and thus remove the need to implement it as a free function=
and be careful when using it.</p><p><br></p></div></blockquote></div></div=
></div></blockquote></div></div></blockquote><div>This is a pretty convinci=
ng argument.</div><div>Perhaps a paper for Toronto?</div><div>Let me know i=
f you'd like any input.</div></div></blockquote><div><br>Well, to begin=
with, I do not completely understand what you are looking for. Would you l=
ike me to write some sort of LaTeX article describing the proposals that I&=
#39;ve outlined here in a more formal way?<br><br>=C2=A0</div><blockquote c=
lass=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px=
#ccc solid;padding-left: 1ex;"><div dir=3D"auto"><div></div><blockquote ty=
pe=3D"cite"><div><div dir=3D"ltr"><blockquote class=3D"gmail_quote" style=
=3D"margin:0;margin-left:0.8ex;border-left:1px #ccc solid;padding-left:1ex"=
><div dir=3D"ltr"><div><div class=3D"gmail_quote"><blockquote class=3D"gmai=
l_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left-width:1px;border-lef=
t-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div dir=
=3D"ltr"><p>With this simple change, shameless people who want to get their=
hands dirty and extract a mutable pointer from a const wrapper will then n=
eed to do some extra work, as should be expected of them in my opinion:</p>=
<p><br></p><div style=3D"background-color:rgb(250,250,250);border:1px solid=
rgb(187,187,187)"><code><div><span style=3D"color:rgb(0,0,0)"></span><span=
style=3D"color:rgb(0,0,136)">using</span><span style=3D"color:rgb(0,0,0)">=
</span><span style=3D"color:rgb(102,0,102)">ConstPropagator</span><span st=
yle=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,102,0)">=3D</=
span><span style=3D"color:rgb(0,0,0)"> std</span><span style=3D"color:rgb(1=
02,102,0)">::</span><span style=3D"color:rgb(0,0,0)">propagate_const</span>=
<span style=3D"color:rgb(102,102,0)"><</span><span style=3D"color:rgb(0,=
0,0)">std</span><span style=3D"color:rgb(102,102,0)">::</span><span style=
=3D"color:rgb(0,0,0)">shar<wbr>ed_ptr</span><span style=3D"color:rgb(102,10=
2,0)"><</span><span style=3D"color:rgb(0,0,0)">T</span><span style=3D"co=
lor:rgb(102,102,0)">>>;</span><span style=3D"color:rgb(0,0,0)"><br><b=
r></span><span style=3D"color:rgb(0,0,136)">const</span><span style=3D"colo=
r:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,0,102)">ConstPropagator<=
/span><span style=3D"color:rgb(102,102,0)">&</span><span style=3D"color=
:rgb(0,0,0)"> immutableRef </span><span style=3D"color:rgb(102,102,0)">=3D<=
/span><span style=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(102=
,102,0)">...</span><span style=3D"color:rgb(0,0,0)"> </span><span style=3D"=
color:rgb(102,102,0)">;</span><span style=3D"color:rgb(0,0,0)"><br>std</spa=
n><span style=3D"color:rgb(102,102,0)">::</span><span style=3D"color:rgb(0,=
0,0)">shared_ptr</span><span style=3D"color:rgb(102,102,0)"><</span><spa=
n style=3D"color:rgb(0,0,0)">T</span><span style=3D"color:rgb(102,102,0)">&=
gt;</span><span style=3D"color:rgb(0,0,0)"> mutableRef </span><span style=
=3D"color:rgb(102,102,0)">=3D</span><span style=3D"color:rgb(0,0,0)"> </spa=
n><span style=3D"color:rgb(0,0,136)">const_cast</span><span style=3D"color:=
rgb(102,102,0)"><</span><span style=3D"color:rgb(102,0,102)">ConstPropag=
ator</span><span style=3D"color:rgb(102,102,0)">*>(</span><span style=3D=
"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,102,0)">&</span=
><span style=3D"color:rgb(0,0,0)">immutableRef </span><span style=3D"color:=
rgb(102,102,0)">)-></span><span style=3D"color:rgb(0,0,0)">get_underlyin=
g</span><span style=3D"color:rgb(102,102,0)">();</span></div></code></div><=
/div><span><font color=3D"#888888">
<p></p>
-- <br>
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br>
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a rel=3D"nofollow">std-proposal...@isocpp.org</a>.<br>
To post to this group, send email to <a rel=3D"nofollow">std-pr...@isocpp.o=
rg</a>.<br>
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/9ad3f22a-a619-4488-bf1e-303641c968b9%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter" rel=3D"nofollow" t=
arget=3D"_blank" onmousedown=3D"this.href=3D'https://groups.google.com/=
a/isocpp.org/d/msgid/std-proposals/9ad3f22a-a619-4488-bf1e-303641c968b9%40i=
socpp.org?utm_medium\x3demail\x26utm_source\x3dfooter';return true;" on=
click=3D"this.href=3D'https://groups.google.com/a/isocpp.org/d/msgid/st=
d-proposals/9ad3f22a-a619-4488-bf1e-303641c968b9%40isocpp.org?utm_medium\x3=
demail\x26utm_source\x3dfooter';return true;">https://groups.google.com=
/a/<wbr>isocpp.org/d/msgid/std-<wbr>proposals/9ad3f22a-a619-4488-<wbr>bf1e-=
303641c968b9%40isocpp.org</a><wbr>.<br>
</font></span></blockquote></div><br></div></div>
</blockquote></div>
<p></p>
-- <br>
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br>
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"javascript:" target=3D"_blank" gdf-obfuscated-mailto=3D"=
NhQZldkPAgAJ" rel=3D"nofollow" onmousedown=3D"this.href=3D'javascript:&=
#39;;return true;" onclick=3D"this.href=3D'javascript:';return true=
;">std-proposal...@<wbr>isocpp.org</a>.<br>
To post to this group, send email to <a href=3D"javascript:" target=3D"_bla=
nk" gdf-obfuscated-mailto=3D"NhQZldkPAgAJ" rel=3D"nofollow" onmousedown=3D"=
this.href=3D'javascript:';return true;" onclick=3D"this.href=3D'=
;javascript:';return true;">std-pr...@isocpp.org</a>.<br>
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/189d5497-36bb-4c9c-8992-13d7eaa36b6b%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter" target=3D"_blank" =
rel=3D"nofollow" onmousedown=3D"this.href=3D'https://groups.google.com/=
a/isocpp.org/d/msgid/std-proposals/189d5497-36bb-4c9c-8992-13d7eaa36b6b%40i=
socpp.org?utm_medium\x3demail\x26utm_source\x3dfooter';return true;" on=
click=3D"this.href=3D'https://groups.google.com/a/isocpp.org/d/msgid/st=
d-proposals/189d5497-36bb-4c9c-8992-13d7eaa36b6b%40isocpp.org?utm_medium\x3=
demail\x26utm_source\x3dfooter';return true;">https://groups.google.com=
/a/<wbr>isocpp.org/d/msgid/std-<wbr>proposals/189d5497-36bb-4c9c-<wbr>8992-=
13d7eaa36b6b%40isocpp.org</a><wbr>.<br>
</div></blockquote></div></blockquote></div>
<p></p>
-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/c0bef11f-1eba-48a2-b11d-8678207822ca%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/c0bef11f-1eba-48a2-b11d-8678207822ca=
%40isocpp.org</a>.<br />
------=_Part_179_278325470.1488019280402--
------=_Part_178_123620501.1488019280400--
.
Author: Jonathan Coe <jonathanbcoe@gmail.com>
Date: Sat, 25 Feb 2017 12:09:53 +0000
Raw View
--Apple-Mail-65258858-2936-412D-99F5-0DCE3F2E0D90
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
> On 25 Feb 2017, at 10:41, Hadrien Grasland <hadrien.grasland@gmail.com> w=
rote:
>=20
>=20
>=20
> Le vendredi 24 f=C3=A9vrier 2017 17:32:29 UTC+1, Jonathan Coe a =C3=A9cri=
t :
>>=20
>>=20
>>=20
>> On 24 Feb 2017, at 16:06, Hadrien Grasland <hadrien....@gmail.com> wrote=
:
>>=20
>>>=20
>>>=20
>>> Le mercredi 22 f=C3=A9vrier 2017 09:56:10 UTC+1, Jonathan Coe a =C3=A9c=
rit :
>>>>=20
>>>>=20
>>>> On 21 February 2017 at 14:54, Hadrien Grasland wrote:
>>>>> Hi everyone,
>>>>>=20
>>>>> I am not sure what this group's policy is regarding resurrection of o=
ld threads, so I have decided to create a new one. But I would like to seco=
nd two comments that were previously made on the propagate_const proposal o=
f N4617:
>>>>>=20
>>>>> 1/ That it should be possible to copy a propagate_const<T> wrapper ( =
https://groups.google.com/a/isocpp.org/forum/#!searchin/std-proposals/propa=
gate_const/std-proposals/1uDKcA9bssU/AZ-aQNAZAAAJ ).
>>>>> 2/ That propagate_const<T> should be convertible to a const-correct f=
orm of the underlying pointer type ( https://groups.google.com/a/isocpp.org=
/forum/#!searchin/std-proposals/propagate_const/std-proposals/7rSMtvQVASk/n=
iLSKtkcBwAJ ).
>>>>>=20
>>>>> Let me motivate, through some concrete use cases, why I think that bo=
th of these changes are necessary.
>>>>>=20
>>>>>=20
>>>>> Need for shallow copy operations
>>>>>=20
>>>>> There are many reasons to use pointers in C++. A very common use case=
, however, is dynamic polymorphism:
>>>>> Different implementations of a virtual interface may have different s=
izes
>>>>> C++, in its commendable quest for zero-cost abstraction, does not pro=
vide native support for dynamically sized types
>>>>> When combined, these two language design choices lead to the conclusi=
on that if the set of implementations of an interface is not known as compi=
le time, polymorphism intrinsically requires pointer indirection in C++
>>>>> For this use case, pointer constness semantics are inadequate. If you=
use a pointer-to-const, you cannot modify the target object, which is ofte=
n fine but tends to be excessively limitating for some use cases, particula=
rly when implementing constructors and factory functions. If, on the other =
hand, you use a pointer-to-mutable, you open a const-correctness hole by al=
lowing yourself to mutate a const object without any compiler warning. Quit=
e frankly, none of these options are very appealing.
>>>>>=20
>>>>> // Pointer-to-const restricts some valid use cases of polymorphism
>>>>> class FirstTry
>>>>> {
>>>>> private:
>>>>> std::unique_ptr<const Interface> m_ptr;
>>>>>=20
>>>>> public:
>>>>> FirstTry()
>>>>> : m_ptr{ std::make_unique<Implementation>( ... ) }
>>>>> {
>>>>> m_ptr->setParent( *this ); // ERROR: Can't mutate through a cons=
t pointer!
>>>>> }
>>>>> };
>>>>>=20
>>>>>=20
>>>>> // Pointer-to-mutable breaks const correctness of polymorphism
>>>>> class SecondTry
>>>>> {
>>>>> private:
>>>>> std::unique_ptr<Interface> m_ptr;
>>>>>=20
>>>>> public:
>>>>> SecondTry()
>>>>> : m_ptr{ std::make_unique<Implementation>( ... ) }
>>>>> {
>>>>> m_ptr->setParent( *this ); // This is now okay...
>>>>> }
>>>>>=20
>>>>> int someRandomAccessor() const {
>>>>> m_ptr->setSomething(); // ...but unfortunately, this is okay too=
:-(
>>>>> return 42;
>>>>> }
>>>>> };
>>>>>=20
>>>>> Now, if we are on the same wavelength, and I will assume in the follo=
wing that this is the case, this is precisely the kind of issue that propag=
ate_const is designed to help with.
>>>>>=20
>>>>> Let us now turn our attention to copy operations. Ideally, polymorphi=
c objects should be deep-copyable, just like any regular value type. Unfort=
unately, in another application of the zero cost abstraction principle, C++=
provides no easy way to do this. Anyone who wants to combine value semanti=
cs and dynamic polymorphism needs to provide explicit support for deep copi=
es across the entire class hierarchy, which is cumbersome to begin with and=
can get problematic when interacting with third-party libraries.
>>>>=20
>>>> Arthur has addressed your concerns with the same points I would have r=
aised (thanks Arthur). On the related topic of deep-copies and const-propag=
ation we're working on polymorphic_value which aims to tackle copies throug=
h class heirarchies. I'd be interested if you have a use case where one of =
propagate_const and polymorphic_value does not solve your problem.=20
>>>>=20
>>>> https://github.com/jbcoe/polymorphic_value/blob/master/talks/2017_1_25=
_cxx_london.md
>>>> https://github.com/jbcoe/polymorphic_value/blob/master/draft.md
>>>=20
>>> I love the idea of polymorphic_value. I think that once available, it s=
hould be taught to new C++ programmers as the standard way to manipulate po=
lymorphic objects, and that anyone should strongly consider using it for th=
is purpose in new codebases, or whenever a mature codebase gets completely =
rewritten.
>>>=20
>>> One thing which I do not think polymorphic_value can do, however, is be=
gradually introduced in an existing codebase. By design, polymorphic_value=
has to be constructed from pointer-to-derived,
>>=20
>> Please construct it from a value, using the factory method or roll your =
own factory method. The pointer constructor is a 'sharp knife'.
>>=20
>>=20
>>> and is thus fundamentally incompatible with the massive body of existin=
g C++ code that uses pointer-to-base for polymorphism.
>>=20
>> Can you elaborate here? Maybe an example would be helpful.
>=20
> I am working on a codebase that was mostly written in the late 90s and ea=
rly 2000s, back when object-oriented programming was going to save the worl=
d and you couldn't write a single line of code without putting it behind a =
virtual interface. Overall, it's a couple millions of lines of code, writte=
n by a couple hundred people, many of which lacked training in basic softwa=
re engineering and have since left the project. So you know, business as us=
ual: let's focus on one tiny bit after another, and improve things one baby=
step at a time, while doing our best to keep that monster alive.
>=20
> As an extra challenge, we're adding threads to this thing, which was obvi=
ously never designed for that. Historically, "we'll just run one sequential=
process per CPU core" bought us a lot of time, but ultimately we needed to=
stop as RAM/core went down and RAM usage went up. I have thus decided to f=
ocus a bit on the issue of const-correctness, in an attempt to contain the =
areas that can do damage in a multithreaded environment.
>=20
> One of the tentacles of this octopus that I'm working on is a scientific =
data analysis package that starts from a 3D point cloud and attempts to ext=
ract helicoidal features from it, in a fashion that is tolerant to both fal=
se positives and negatives in the input dataset. The point cloud is located=
on a fairly complex model of the detector, which in software is fully impl=
emented using class hierarchies. You have a class hierarchy for volumes, a =
class hierarchy for materials, a class hierarchy for surfaces, a class hier=
archy for layers (mostly a group of volumes with a bounding box), and so on=
, and all these classes cross-reference one another using pointer-to-base-c=
lass, sometimes raw (e.g. Surface*), sometimes owning (e.g. shared_ptr<Surf=
ace>).
>=20
> If I take the Surface class hierarchy as an example, a quick search for "=
Surface*", "shared_ptr<Surface>" and "shared_ptr<const Surface>", where Sur=
face is the base class of all concrete surface classes, leads a couple of h=
undreds of results. Several of these reside in public interfaces, both as i=
nputs and as outputs, and changing them could have broader implication for =
clients that I'm not aware of. Outputs are not a problem: I can just take a=
pointer to the contents of polymorphic value. But inputs, as far as I can =
tell, are a much bigger issue: if a client ever gives us a Surface*, well, =
polymorphic_value will not be able to help us.
>=20
> This is just one example of a single class hierarchy, in a single package=
of a big codebase. And it's far from the worst package that we have, peopl=
e have already spent quite a lot of time cleaning it up. But it does not ma=
ke me very optimistic about the viability of migrating to polymorphic_value=
in this kind of situation.
>=20
> =20
>>> Once you introduce a polymorphic_value somewhere, you also need to rewr=
ite every other piece of client code that uses the same class hierarchy, of=
ten including third-party libraries that you may have no easy access to. Ev=
en though the final code will be cleaner, the process will be even more cos=
tly as adding clone() methods everywhere. And for large projects, that will=
just not be seen as a viable endeavour.
>>>=20
>>=20
>> I've not found this to be the case though, like many things, it probably=
depends on patterns at use in the code.
>>=20
>>> I believe that with the modifications that I am proposing, propagate_co=
nst would be a better fit for such refactoring purposes. With it, one could=
progressively introduce const-correct pointer manipulation at relatively l=
ittle cost. Sure, you wouldn't have deep copies, but hey, developers of exi=
sting codebases never had them to begin with. So we aren't going to miss th=
em in a context of progressive code evolution. And if error-prone shallow c=
opies are harder to perform by accident, well, all the better.
>>>=20
>>> =20
>>>=20
>>>>>=20
>>>>> class Interface
>>>>> {
>>>>> public:
>>>>> // This kind of boilerplate must be added to every class supporting=
deep copy...
>>>>> virtual std::unique_ptr<Interface> clone() const =3D 0;
>>>>> };
>>>>>=20
>>>>> class Implementation : public Interface
>>>>> {
>>>>> public:
>>>>> // ...and replicated again and again in every single non-abstract c=
hild. No fun.
>>>>> std::unique_ptr<Interface> clone() const final override
>>>>> {
>>>>> return std::make_unique<Implementation>( *this );
>>>>> }
>>>>> };
>>>>>=20
>>>>> But thankfully, sometimes, we can do without a true deep copy, and sa=
tisfy ourselves with a shallow copy. And in fact, for some use cases, a sha=
llow copy will even be exactly what we want:
>>>>> When building some variety of search acceleration structure (hashmap,=
neighbour locator, axis-aligned-bounding box...)
>>>>> When many "slave objects" share a reference to some common, potential=
ly large "master object"
>>>>> Unfortunately, even though all standard C++ pointer types make it tri=
vial to shallow-copy a polymorphic object, propagate_const gets in our way =
here by being move only. I see this design choice as a shortcoming, because=
it reduces the usefulness of propagate_const in many dynamic polymorphism =
use cases where the underlying pointer type would have done just fine. For =
example, without shallow copy operations...
>>>>> You cannot let the compiler implement a shallow copy constructor for =
you using "=3D default"
>>>>> You cannot use std::copy_if to find objects matching some predicate i=
n an internal dataset
>>>>> You cannot easily build object search acceleration structures, such a=
s hashmaps or AABBs
>>>>> You cannot easily share a reference to an object wrapped by propagate=
_const with other objects
>>>>> I am aware that shallow copies are possible using the get() and get_u=
nderlying() operations. I do not see this as a satisfactory answer, because=
it breaks every STL-ish algorithm that expects a copy constructor. Of cour=
se, I could build a propagate_const wrapper that has a copy constructor mys=
elf, and this is what I would end up doing if propagate_const were accepted=
in the STL in its current form. But I think that the use cases that I ment=
ioned above are valid enough to warrant changing the design of propagate_co=
nst instead.
>>>>>=20
>>>>> I am aware that in order to be const-correct, a shallow copy construc=
tor for propagate_const would need to operate from a non-const reference. I=
am fine with this tradeoff. The main reason we usually allow ourselves to =
make mutable copies from const objects is because we assume the copy to be =
independent from the original object. This assumption is broken when making=
shallow copies through pointer or reference types. Sure, many APIs will be=
broken initially, but that is unavoidable when fixing old programming lang=
uage flaws. Bugs will be reported, and interfaces will be fixed, the way it=
's always been done.
>>>>>=20
>>>>>=20
>>>>>=20
>>>>> For prior art, if you look at the STL's documentation, you will find =
that customizable algorithms that copy const data, such as std::copy_if, ex=
plicitly do NOT require the user-provided functions to consume the data by =
const-reference. So the idea of copying from non-const is not new. The C++ =
community only needs to re-discover it.
>>>>>=20
>>>>>=20
>>>>>=20
>>>>=20
>>>> We considered making copies from non-const propagate_const legal but w=
ere strongly dissuaded by 3 major standard library implementers.
>>>=20
>>> Sure, they disagreed. But what were their argument for it? Was it just =
a case of "the maffia said no"?
>>>=20
>>> =20
>>>> =20
>>>>> I am also aware of the objection, made in a thread linked above, that=
"We were not confident that a non-const copy constructor would protect the=
user from accidental loss of const and accidental mutable shared state". A=
s far as I'm concerned, this hand-waving statement was not properly justifi=
ed. From my perspective, the proposed alternative of using get_underlying s=
eems to be much bigger breach of const-correctness than a copy constructor =
that operates from mutable, because from a semantic point of view, get_unde=
rlying is essentially a silent const_cast that standard code analysis tools=
won't catch.
>>>>>=20
>>>>>=20
>>>>>=20
>>>>=20
>>>> We added `get_underlying` as an nicer alternative to `reinterpret_cast=
`, which would give access to the underlying pointer.
>>>>=20
>>>> Our design sought to avoid silent surprise. Granted a user can do the =
wrong thing with get_underlying but the code is written by the user, not by=
the compiler. Forcing the user to be explicit offers some protection.
>>>=20
>>> Been there, done that. When you see all the tools that C++ and its stan=
dard library provide to help you shoot yourself in the foot, it is always v=
ery tempting to roll your own, thinking that surely it will do the job bett=
er. Except usually, it doesn't. When what you want is a cast, make it a cas=
t, don't try to make it look nicer by burying it in custom abstractions.
>>>=20
>>> Let's take a step back and think about this from an API design point of=
view. Why makes get_underlying such an poor interface?
>>>=20
>>> 1/ By returning a reference to the internal pointer object, you leak im=
plementation details
>>> 2/ By leaking implementation details, you lose the ability to maintain =
the single most important class invariant of propagate_const, which is that=
type-safe accesses to a const propagate_const wrapper should not give one =
write access to the internal object.
>>>=20
>>> Now, I am aware that we are discussing C++ API design here. Leaking imp=
lementation details and violating class invariants is okay, as long as it c=
an give me that 0.1% performance improvement in some very obscure use case =
that someone thought about someday. One a single compiler. With the right c=
ombination of flags. Until the next release. Perhaps.
>>>=20
>>> But in the case of get_underlying, you could actually leak these implem=
entation details, if you really wanted to, without violating your class inv=
ariants along the way. The fix is simple, really. Make get_underlying a non=
-const method of the propagate_const object, and drop get_underlying from c=
onst. Be serious about interface guarantees.
>>>=20
>>> If your users come whining because they can't easily extract a pointer-=
to-mutable from a const object, try to give them the talk about const corre=
ctness. If they won't listen, tell them to const_cast the propagate_const w=
rapper. But don't compromise your API just to make stupid operations more c=
onvenient to write. It is never worth it.
>>>=20
>>>=20
>>> =20
>>>>>=20
>>>>> Const-correct convertibility to the underlying pointer type
>>>>>=20
>>>>> Sometimes, one has to extract a pointer back from the propagate_const=
wrapper. I would see two main use cases for this:
>>>>> To feed a legacy API that is not (yet?) compatible with propagate_con=
st.
>>>>> To turn a propagate_const<T> into a pointer-to-const, when the recipi=
ent should not be able to modify to the target object
>>>>> The current propagate_const interface provides three ways to do this:
>>>>>=20
>>>>> Call get() and get a raw pointer
>>>>> Rely on an implicit cast to raw pointer, if available
>>>>> Use get_underlying to access the internals of propagate_const.
>>>>> As I'm going to elaborate, the first two operations are not always th=
e right tool for the job at hand, because they fail to convey important own=
ership information from the underlying pointer type. Whereas get_underlying=
, as currently implemented, is a flawed interface that goes directly agains=
t the design goals of propagate_const and should be eradicated before stand=
ardization.
>>>>>=20
>>>>>=20
>>>>>=20
>>>>> As a use case, suppose that as discussed previously, I am working a "=
slave object", which holds a shared_ptr to an associated "master object" th=
at it shares with other slaves. To improve the const-correctness of this de=
sign, I have decided to refactor the shared_ptr into a propagate_const<shar=
ed_ptr>. So far, so good.
>>>>>=20
>>>>>=20
>>>>>=20
>>>>> class Master
>>>>> {
>>>>> ...
>>>>> };
>>>>>=20
>>>>> class Slave
>>>>> {
>>>>> private:
>>>>> // This was refactored from "std::shared_ptr<Master> m_master"
>>>>> std::propagate_const<std::shared_ptr<Master>> m_master;
>>>>>=20
>>>>> public:
>>>>> Slave( std::shared_ptr<Master> master )
>>>>> : m_master(master)
>>>>> {
>>>>> m_master->addSlave( *this );
>>>>> }
>>>>>=20
>>>>> ...
>>>>> };=20
>>>>> But as I proceed with the refactoring, I discover that the Slave clas=
s used to provide a method that shares access to its master:
>>>>>=20
>>>>> std::shared_ptr<Master> Slave::getMaster() const
>>>>> {
>>>>> return m_master;
>>>>> }
>>>>>=20
>>>>> This does not compile anymore. And I'm happy about that: it should ne=
ver have compiled to begin with. Returning non-const access to my members f=
rom a const method definitely does not match my idea of const-correctness!
>>>>>=20
>>>>> Instead, I would like to only provide const access to the master obje=
ct, like so:
>>>>>=20
>>>>> std::shared_ptr<const Master> Slave::getMaster() const;
>>>>>=20
>>>>> If my clients are well-behaved and do not mutate the master object, t=
his will be a minimally invasive interface change. It will require only min=
or client rewrites, the kind that could be automated by sed or an IDE. It c=
ould even require no client rewrite at all if I end up being lucky and have=
clients that were using auto and friends.
>>>>>=20
>>>>> Unfortunately, the current propagate_const interface does not allow m=
e to implement this method in a clean way.
>>>>>=20
>>>>=20
>>>> Sadly that's intended. We can't guarantee that "clients are well-behav=
ed and do not mutate the master object".
>>>=20
>>> And you do not need to, that's my job. You give me a well-designed cons=
t-correct pointer wrapper, and I'll use it to improve the const-correctness=
of the codebase that I'm responsible of. If I find broken client code, I w=
ill do my best to fix it myself, and send an angry e-mail to the person res=
ponsible if I don't manage. It's called maintenance.
>>>=20
>>> But when I'm going through the pain of doing this, I would like to do s=
o using quality tools that give me the impression that the effort is worthw=
hile, and won't be undermined on the day after by someone trying to do some=
thing stupid, going through the STL documentation and thinking "oh, I know,=
I'll just use get_underlying...".
>>>=20
>>> When people start to break the thread-safety of our code by introducing=
mutation in places where it doesn't belong, I want them to explicitly writ=
e down the dreaded word "cast" and feel that chill going down their spine, =
telling them that they're doing something wrong. I want them to figure out =
that the basic design of their code is broken, and to understand that they =
need to fix it before submitting it upstream. I don't want the only line of=
defense against software chaos to be me telling them at the end, when they=
finally submit their merge request, that I can't accept it into our codeba=
se and they need to rewrite half of it.
>>>=20
>>> Good interfaces save everyone's time by making bad things look bad. Let=
's have more of them in C++.
>>>=20
>>> =20
>>>> =20
>>>>> The obvious code snippet would not work due to the lack of an appropr=
iate implicit conversion:
>>>>>=20
>>>>> std::shared_ptr<const Master> Slave::getMaster() const
>>>>> {
>>>>> return m_master; // ERROR: No implicit conversion from propagate_c=
onst<shared_ptr>!
>>>>> }
>>>>>=20
>>>>> Using get() here would be the perfect way to introduce use after free=
bugs:
>>>>>=20
>>>>> std::shared_ptr<const Master> Slave::getMaster() const
>>>>> {
>>>>> const Master* master =3D m_master.get();
>>>>> return std::shared_ptr<Master>( master ); // INCORRECT: Two owners=
for one object!
>>>>> }
>>>>>=20
>>>>> And using get_underlying will both break const correctness and fail t=
o provide me with the correct return type:
>>>>>=20
>>>>> std::shared_ptr<const Master> Slave::getMaster() const
>>>>> {
>>>>> auto master =3D std::get_underlying( m_master ); // This is an std=
::shared_ptr<Master>
>>>>> return master; // ERROR: No implicit conversion to std::shared_ptr=
<const Master>!
>>>>> }
>>>>>=20
>>>>> To implement this method, in addition to the const-incorrect get_unde=
rlying interface, I would also need a const cast!
>>>>>=20
>>>>> std::shared_ptr<const Master> Slave::getMaster() const
>>>>> {
>>>>> auto master =3D std::get_underlying( m_master );=20
>>>>> return std::const_pointer_cast<const Master>( master );
>>>>> }
>>>>>=20
>>>>>=20
>>>>> For sure, I would never want an abomination like this to pass code re=
view :)
>>>>>=20
>>>>>=20
>>>>>=20
>>>> Agreed.
>>>> =20
>>>>> This little thought experiment showcases two major issues with get_un=
derlying:
>>>>>=20
>>>>> Counter to the goal of of propagate_const, it makes it trivial to vio=
late const-correctness without the compiler noticing (or, for that matter, =
any developer or static analysis tool that is unfamiliar with the propagate=
_const API).
>>>>> By not returning the const-correct type, get_underlying can make it u=
nnecessarily hard to implement const-correct interfaces.
>>>>> The authors of the original propagate_const proposal were aware that =
the get_underlying interface was suboptimal, and for this reason they opted=
to make it less usable by hiding it as a free function instead of making i=
t a proper method of propagate_const. But personnally, I would go further: =
drop get_underlying altogether, and replace it with something that is both =
const-correct and respectful of pointer ownership issues.
>>>>>=20
>>>>>=20
>>>>>=20
>>>>> My counter-proposal would be to have a propagate_const method, maybe =
called "share()", which enables sharing access to the data pointed by propa=
gate_const with due respect paid to both const-correctness and the underlyi=
ng pointer's ownership semantics. In practice:
>>>>>=20
>>>>> If called on propagate_const<T*>, share() should return a T*
>>>>> If called on const propagate_const<T*>, share() should return a const=
T*
>>>>> If called on propagate_const<shared_ptr<T>>, share() should return a =
shared_ptr<T>
>>>>> If called on const propagate_const<shared_ptr<T>>, share() should ret=
urn a shared_ptr<const T>
>>>>> share() should not be defined on propagate_const<unique_ptr<T>> since=
that pointer does not support sharing ownership
>>>>> In addition, as a convenience, whenever a share() method exists, an i=
mplicit cast could be provided to the underlying pointer type as a way to m=
ake such ownership sharing more convenient and to enable things like compar=
ison of containers of shared_ptr<T> with containers of propagate_const<shar=
ed_ptr<T>> through standard (STL-ish) algorithms.
>>>>>=20
>>>>>=20
>>>>>=20
>>>>> If you really, positively want something like get_underlying to exist=
, it should only be applicable to non-const object. This alone would make i=
t const-correct, and thus remove the need to implement it as a free functio=
n and be careful when using it.
>>>>>=20
>>>>>=20
>>>>>=20
>> This is a pretty convincing argument.
>> Perhaps a paper for Toronto?
>> Let me know if you'd like any input.
>=20
> Well, to begin with, I do not completely understand what you are looking =
for. Would you like me to write some sort of LaTeX article describing the p=
roposals that I've outlined here in a more formal way?
>=20
> =20
Having thought a bit more about your issue I'm intending to write something=
myself and will put your name on it. I'll contact you off-list.
>>>>> With this simple change, shameless people who want to get their hands=
dirty and extract a mutable pointer from a const wrapper will then need to=
do some extra work, as should be expected of them in my opinion:
>>>>>=20
>>>>>=20
>>>>>=20
>>>>> using ConstPropagator =3D std::propagate_const<std::shared_ptr<T>>;
>>>>>=20
>>>>> const ConstPropagator& immutableRef =3D ... ;
>>>>> std::shared_ptr<T> mutableRef =3D const_cast<ConstPropagator*>( &immu=
tableRef )->get_underlying();
>>>>> --=20
>>>>> You received this message because you are subscribed to the Google Gr=
oups "ISO C++ Standard - Future Proposals" group.
>>>>> To unsubscribe from this group and stop receiving emails from it, sen=
d an email to std-proposal...@isocpp.org.
>>>>> To post to this group, send email to std-pr...@isocpp.org.
>>>>> To view this discussion on the web visit https://groups.google.com/a/=
isocpp.org/d/msgid/std-proposals/9ad3f22a-a619-4488-bf1e-303641c968b9%40iso=
cpp.org.
>>>>=20
>>>=20
>>> --=20
>>> You received this message because you are subscribed to the Google Grou=
ps "ISO C++ Standard - Future Proposals" group.
>>> To unsubscribe from this group and stop receiving emails from it, send =
an email to std-proposal...@isocpp.org.
>>> To post to this group, send email to std-pr...@isocpp.org.
>>> To view this discussion on the web visit https://groups.google.com/a/is=
ocpp.org/d/msgid/std-proposals/189d5497-36bb-4c9c-8992-13d7eaa36b6b%40isocp=
p.org.
>=20
> --=20
> You received this message because you are subscribed to the Google Groups=
"ISO C++ Standard - Future Proposals" group.
> To unsubscribe from this group and stop receiving emails from it, send an=
email to std-proposals+unsubscribe@isocpp.org.
> To post to this group, send email to std-proposals@isocpp.org.
> To view this discussion on the web visit https://groups.google.com/a/isoc=
pp.org/d/msgid/std-proposals/c0bef11f-1eba-48a2-b11d-8678207822ca%40isocpp.=
org.
--=20
You received this message because you are subscribed to the Google Groups "=
ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp=
..org/d/msgid/std-proposals/752494EE-1737-4F41-B490-E951105527C2%40gmail.com=
..
--Apple-Mail-65258858-2936-412D-99F5-0DCE3F2E0D90
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<html><head><meta http-equiv=3D"content-type" content=3D"text/html; charset=
=3Dutf-8"></head><body dir=3D"auto"><div></div><div><br></div><div><br>On 2=
5 Feb 2017, at 10:41, Hadrien Grasland <<a href=3D"mailto:hadrien.grasla=
nd@gmail.com">hadrien.grasland@gmail.com</a>> wrote:<br><br></div><block=
quote type=3D"cite"><div><div dir=3D"ltr"><br><br>Le vendredi 24 f=C3=A9vri=
er 2017 17:32:29 UTC+1, Jonathan Coe a =C3=A9crit :<blockquote class=
=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #cc=
c solid;padding-left: 1ex;"><div dir=3D"auto"><div></div><div><br></div><di=
v><br>On 24 Feb 2017, at 16:06, Hadrien Grasland <<a href=3D"javascript:=
" target=3D"_blank" gdf-obfuscated-mailto=3D"NhQZldkPAgAJ" rel=3D"nofollow"=
onmousedown=3D"this.href=3D'javascript:';return true;" onclick=3D"this.hre=
f=3D'javascript:';return true;">hadrien....@gmail.com</a>> wrote:<br><br=
></div><blockquote type=3D"cite"><div><div dir=3D"ltr"><br><br>Le mercredi =
22 f=C3=A9vrier 2017 09:56:10 UTC+1, Jonathan Coe a =C3=A9crit :<block=
quote class=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-left=
:1px #ccc solid;padding-left:1ex"><div dir=3D"ltr"><div><br><div class=3D"g=
mail_quote">On 21 February 2017 at 14:54, Hadrien Grasland wrote:<br><block=
quote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left-w=
idth:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding=
-left:1ex"><div dir=3D"ltr">Hi everyone,<br><br>I am not sure what this gro=
up's policy is regarding resurrection of old threads, so I have decided to =
create a new one. But I would like to second two comments that were previou=
sly made on the propagate_const proposal of N4617:<br><br>1/ That it should=
be possible to copy a propagate_const<T> wrapper ( <a href=3D"https:=
//groups.google.com/a/isocpp.org/forum/#!searchin/std-proposals/propagate_c=
onst/std-proposals/1uDKcA9bssU/AZ-aQNAZAAAJ" rel=3D"nofollow" target=3D"_bl=
ank" onmousedown=3D"this.href=3D'https://groups.google.com/a/isocpp.org/for=
um/#!searchin/std-proposals/propagate_const/std-proposals/1uDKcA9bssU/AZ-aQ=
NAZAAAJ';return true;" onclick=3D"this.href=3D'https://groups.google.com/a/=
isocpp.org/forum/#!searchin/std-proposals/propagate_const/std-proposals/1uD=
KcA9bssU/AZ-aQNAZAAAJ';return true;">https://groups.google.com/a/<wbr>isocp=
p.org/forum/#!searchin/<wbr>std-proposals/propagate_const/<wbr>std-proposal=
s/1uDKcA9bssU/AZ-<wbr>aQNAZAAAJ</a> ).<br>2/ That propagate_const<T> =
should be convertible to a const-correct form of the underlying pointer typ=
e ( <a href=3D"https://groups.google.com/a/isocpp.org/forum/#!searchin/std-=
proposals/propagate_const/std-proposals/7rSMtvQVASk/niLSKtkcBwAJ" rel=3D"no=
follow" target=3D"_blank" onmousedown=3D"this.href=3D'https://groups.google=
..com/a/isocpp.org/forum/#!searchin/std-proposals/propagate_const/std-propos=
als/7rSMtvQVASk/niLSKtkcBwAJ';return true;" onclick=3D"this.href=3D'https:/=
/groups.google.com/a/isocpp.org/forum/#!searchin/std-proposals/propagate_co=
nst/std-proposals/7rSMtvQVASk/niLSKtkcBwAJ';return true;">https://groups.go=
ogle.com/a/<wbr>isocpp.org/forum/#!searchin/<wbr>std-proposals/propagate_co=
nst/<wbr>std-proposals/7rSMtvQVASk/<wbr>niLSKtkcBwAJ</a> ).<br><br>Let me m=
otivate, through some concrete use cases, why I think that both of these ch=
anges are necessary.<br><br><br><font size=3D"4">Need for shallow copy oper=
ations</font><br><br>There are many reasons to use pointers in C++. A very =
common use case, however, is dynamic polymorphism:<br><ul><li>Different imp=
lementations of a virtual interface may have different sizes</li><li>C++, i=
n its commendable quest for zero-cost abstraction, does not provide native =
support for dynamically sized types</li><li>When combined, these two langua=
ge design choices lead to the conclusion that if the set of implementations=
of an interface is not known as compile time, polymorphism intrinsically r=
equires pointer indirection in C++</li></ul>For this use case, pointer cons=
tness semantics are inadequate. If you use a pointer-to-const, you cannot m=
odify the target object, which is often fine but tends to be excessively li=
mitating for some use cases, particularly when implementing constructors an=
d factory functions. If, on the other hand, you use a pointer-to-mutable, y=
ou open a const-correctness hole by allowing yourself to mutate a const obj=
ect without any compiler warning. Quite frankly, none of these options are =
very appealing.<br><br><div style=3D"background-color:rgb(250,250,250);bord=
er:1px solid rgb(187,187,187)"><code><div><span style=3D"color:rgb(136,0,0)=
">// Pointer-to-const restricts some valid use cases of polymorphism</span>=
<span style=3D"color:rgb(0,0,0)"><br></span><span style=3D"color:rgb(0,0,13=
6)">class</span><span style=3D"color:rgb(0,0,0)"> </span><span style=3D"col=
or:rgb(102,0,102)">FirstTry</span><span style=3D"color:rgb(0,0,0)"><br></sp=
an><span style=3D"color:rgb(102,102,0)">{</span><span style=3D"color:rgb(0,=
0,0)"></span><br><code><span style=3D"color:rgb(0,0,136)">private</span><sp=
an style=3D"color:rgb(102,102,0)">:</span><span style=3D"color:rgb(0,0,0)">=
<br> std</span><span style=3D"color:rgb(102,102,0)">::</span><span st=
yle=3D"color:rgb(0,0,0)">unique_ptr</span><span style=3D"color:rgb(102,102,=
0)"><</span><span style=3D"color:rgb(0,0,136)">const</span><span style=
=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,0,102)">Interfac=
e</span><span style=3D"color:rgb(102,102,0)">></span><span style=3D"colo=
r:rgb(0,0,0)"> m_ptr</span><span style=3D"color:rgb(102,102,0)">;</span><sp=
an style=3D"color:rgb(0,0,0)"><br><br></span></code><span style=3D"color:rg=
b(0,0,136)">public</span><span style=3D"color:rgb(102,102,0)">:</span><span=
style=3D"color:rgb(0,0,0)"><br> </span><span style=3D"color:rgb(102,=
0,102)">FirstTry</span><span style=3D"color:rgb(102,102,0)">()</span><span =
style=3D"color:rgb(0,0,0)"><br> </span><span style=3D"color:rg=
b(102,102,0)">:</span><span style=3D"color:rgb(0,0,0)"> m_ptr</span><span s=
tyle=3D"color:rgb(102,102,0)">{</span><span style=3D"color:rgb(0,0,0)"> std=
</span><span style=3D"color:rgb(102,102,0)">::</span><span style=3D"color:r=
gb(0,0,0)">make_unique</span><span style=3D"color:rgb(102,102,0)"><</spa=
n><span style=3D"color:rgb(102,0,102)">Implementatio<wbr>n</span><span styl=
e=3D"color:rgb(102,102,0)">>(</span><span style=3D"color:rgb(0,0,0)"> </=
span><span style=3D"color:rgb(102,102,0)">...</span><span style=3D"color:rg=
b(0,0,0)"> </span><span style=3D"color:rgb(102,102,0)">)</span><span style=
=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,102,0)">}</span>=
<span style=3D"color:rgb(0,0,0)"><br> </span><span style=3D"color:rgb=
(102,102,0)">{</span><span style=3D"color:rgb(0,0,0)"><br> m_p=
tr</span><span style=3D"color:rgb(102,102,0)">-></span><span style=3D"co=
lor:rgb(0,0,0)">setParent</span><span style=3D"color:rgb(102,102,0)">(</spa=
n><span style=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,102=
,0)">*</span><span style=3D"color:rgb(0,0,136)">this</span><span style=3D"c=
olor:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,102,0)">);</span><spa=
n style=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(136,0,0=
)">// ERROR:</span><span style=3D"color:rgb(0,0,0)"> Can't mutate through a=
const pointer!<br> </span><span style=3D"color:rgb(102,102,0)">}</sp=
an><span style=3D"color:rgb(0,0,0)"><br></span><span style=3D"color:rgb(102=
,102,0)">};</span><span style=3D"color:rgb(0,0,0)"><br><br><br></span><span=
style=3D"color:rgb(136,0,0)">// Pointer-to-mutable breaks const correctnes=
s of polymorphism</span><span style=3D"color:rgb(0,0,0)"><br></span><span s=
tyle=3D"color:rgb(0,0,136)">class</span><span style=3D"color:rgb(0,0,0)"> <=
/span><span style=3D"color:rgb(102,0,102)">SecondTry</span><span style=3D"c=
olor:rgb(0,0,0)"><br></span><span style=3D"color:rgb(102,102,0)">{</span><s=
pan style=3D"color:rgb(0,0,0)"><br>private:<br> std::unique_ptr<In=
terface> m_ptr;<br><br></span><span style=3D"color:rgb(0,0,136)">public<=
/span><span style=3D"color:rgb(102,102,0)">:</span><span style=3D"color:rgb=
(0,0,0)"><br> </span><span style=3D"color:rgb(102,0,102)">SecondTry</=
span><span style=3D"color:rgb(102,102,0)">()</span><span style=3D"color:rgb=
(0,0,0)"><br> </span><span style=3D"color:rgb(102,102,0)">:</s=
pan><span style=3D"color:rgb(0,0,0)"> m_ptr</span><span style=3D"color:rgb(=
102,102,0)">{</span><span style=3D"color:rgb(0,0,0)"> std</span><span style=
=3D"color:rgb(102,102,0)">::</span><span style=3D"color:rgb(0,0,0)">make_un=
ique</span><span style=3D"color:rgb(102,102,0)"><</span><span style=3D"c=
olor:rgb(102,0,102)">Implementatio<wbr>n</span><span style=3D"color:rgb(102=
,102,0)">>(</span><span style=3D"color:rgb(0,0,0)"> </span><span style=
=3D"color:rgb(102,102,0)">...</span><span style=3D"color:rgb(0,0,0)"> </spa=
n><span style=3D"color:rgb(102,102,0)">)</span><span style=3D"color:rgb(0,0=
,0)"> </span><span style=3D"color:rgb(102,102,0)">}</span><span style=3D"co=
lor:rgb(0,0,0)"><br> </span><span style=3D"color:rgb(102,102,0)">{</s=
pan><span style=3D"color:rgb(0,0,0)"><br> m_ptr</span><span st=
yle=3D"color:rgb(102,102,0)">-></span><span style=3D"color:rgb(0,0,0)">s=
etParent</span><span style=3D"color:rgb(102,102,0)">(</span><span style=3D"=
color:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,102,0)">*</span><spa=
n style=3D"color:rgb(0,0,136)">this</span><span style=3D"color:rgb(0,0,0)">=
</span><span style=3D"color:rgb(102,102,0)">);</span><span style=3D"color:=
rgb(0,0,0)"> </span><span style=3D"color:rgb(136,0,0)">// This is now=
okay...</span><span style=3D"color:rgb(0,0,0)"><br> </span><span sty=
le=3D"color:rgb(102,102,0)">}</span><span style=3D"color:rgb(0,0,0)"><br><b=
r> </span><span style=3D"color:rgb(0,0,136)">int</span><span style=3D=
"color:rgb(0,0,0)"> someRandomAccessor</span><span style=3D"color:rgb(102,1=
02,0)">()</span><span style=3D"color:rgb(0,0,0)"> </span><span style=3D"col=
or:rgb(0,0,136)">const</span><span style=3D"color:rgb(0,0,0)"> </span><span=
style=3D"color:rgb(102,102,0)">{</span><span style=3D"color:rgb(0,0,0)"><b=
r> m_ptr</span><span style=3D"color:rgb(102,102,0)">-></spa=
n><span style=3D"color:rgb(0,0,0)">setSomething</span><span style=3D"color:=
rgb(102,102,0)">();</span><span style=3D"color:rgb(0,0,0)"> </span><s=
pan style=3D"color:rgb(136,0,0)">// ...but unfortunately, this is okay too =
:-(</span><span style=3D"color:rgb(0,0,0)"><br> </span><span s=
tyle=3D"color:rgb(0,0,136)">return</span><span style=3D"color:rgb(0,0,0)"> =
</span><span style=3D"color:rgb(0,102,102)">42</span><span style=3D"color:r=
gb(102,102,0)">;</span><span style=3D"color:rgb(0,0,0)"><br> </span><=
span style=3D"color:rgb(102,102,0)">}</span><span style=3D"color:rgb(0,0,0)=
"><br>};</span><span style=3D"color:rgb(0,0,0)"><br></span></div></code></d=
iv><br>Now, if we are on the same wavelength, and I will assume in the foll=
owing that this is the case, this is precisely the kind of issue that propa=
gate_const is designed to help with.<br><br>Let us now turn our attention t=
o copy operations. Ideally, polymorphic objects should be deep-copyable, ju=
st like any regular value type. Unfortunately, in another application of th=
e zero cost abstraction principle, C++ provides no easy way to do this. Any=
one who wants to combine value semantics and dynamic polymorphism needs to =
provide explicit support for deep copies across the entire class hierarchy,=
which is cumbersome to begin with and can get problematic when interacting=
with third-party libraries.<br></div></blockquote><div><br></div><div>Arth=
ur has addressed your concerns with the same points I would have raised (th=
anks Arthur). On the related topic of deep-copies and const-propagation we'=
re working on polymorphic_value which aims to tackle copies through class h=
eirarchies. I'd be interested if you have a use case where one of propagate=
_const and polymorphic_value does not solve your problem. </div><div><=
br></div><div><a href=3D"https://github.com/jbcoe/polymorphic_value/blob/ma=
ster/talks/2017_1_25_cxx_london.md" rel=3D"nofollow" target=3D"_blank" onmo=
usedown=3D"this.href=3D'https://www.google.com/url?q\x3dhttps%3A%2F%2Fgithu=
b.com%2Fjbcoe%2Fpolymorphic_value%2Fblob%2Fmaster%2Ftalks%2F2017_1_25_cxx_l=
ondon.md\x26sa\x3dD\x26sntz\x3d1\x26usg\x3dAFQjCNHiVpbjR-rhERETegsvD58f1-r4=
Hw';return true;" onclick=3D"this.href=3D'https://www.google.com/url?q\x3dh=
ttps%3A%2F%2Fgithub.com%2Fjbcoe%2Fpolymorphic_value%2Fblob%2Fmaster%2Ftalks=
%2F2017_1_25_cxx_london.md\x26sa\x3dD\x26sntz\x3d1\x26usg\x3dAFQjCNHiVpbjR-=
rhERETegsvD58f1-r4Hw';return true;">https://github.com/jbcoe/<wbr>polymorph=
ic_value/blob/master/<wbr>talks/2017_1_25_cxx_london.md</a><br></div><div><=
a href=3D"https://github.com/jbcoe/polymorphic_value/blob/master/draft.md" =
rel=3D"nofollow" target=3D"_blank" onmousedown=3D"this.href=3D'https://www.=
google.com/url?q\x3dhttps%3A%2F%2Fgithub.com%2Fjbcoe%2Fpolymorphic_value%2F=
blob%2Fmaster%2Fdraft.md\x26sa\x3dD\x26sntz\x3d1\x26usg\x3dAFQjCNE-iatIgiyM=
pvR4yv-KdAxgAdZuhQ';return true;" onclick=3D"this.href=3D'https://www.googl=
e.com/url?q\x3dhttps%3A%2F%2Fgithub.com%2Fjbcoe%2Fpolymorphic_value%2Fblob%=
2Fmaster%2Fdraft.md\x26sa\x3dD\x26sntz\x3d1\x26usg\x3dAFQjCNE-iatIgiyMpvR4y=
v-KdAxgAdZuhQ';return true;">https://github.com/jbcoe/<wbr>polymorphic_valu=
e/blob/master/<wbr>draft.md</a></div></div></div></div></blockquote><div><b=
r>I love the idea of polymorphic_value. I think that once available, it sho=
uld be taught to new C++ programmers as the standard way to manipulate poly=
morphic objects, and that anyone should strongly consider using it for this=
purpose in new codebases, or whenever a mature codebase gets completely re=
written.<br><br>One thing which I do not think polymorphic_value can do, ho=
wever, is be gradually introduced in an existing codebase. By design, polym=
orphic_value has to be constructed from pointer-to-derived, </div></div></d=
iv></blockquote><div><br></div><div>Please construct it from a value, using=
the factory method or roll your own factory method. The pointer constructo=
r is a 'sharp knife'.</div><div><br></div><br><blockquote type=3D"cite"><di=
v><div dir=3D"ltr"><div>and is thus fundamentally incompatible with the mas=
sive body of existing C++ code that uses pointer-to-base for polymorphism. =
</div></div></div></blockquote><div><br></div><div>Can you elaborate here? =
Maybe an example would be helpful.</div></div></blockquote><div><br>I am wo=
rking on a codebase that was mostly written in the late 90s and early 2000s=
, back when object-oriented programming was going to save the world and you=
couldn't write a single line of code without putting it behind a virtual i=
nterface. Overall, it's a couple millions of lines of code, written by a co=
uple hundred people, many of which lacked training in basic software engine=
ering and have since left the project. So you know, business as usual: let'=
s focus on one tiny bit after another, and improve things one baby step at =
a time, while doing our best to keep that monster alive.<br><br>As an extra=
challenge, we're adding threads to this thing, which was obviously never d=
esigned for that. Historically, "we'll just run one sequential process per =
CPU core" bought us a lot of time, but ultimately we needed to stop as RAM/=
core went down and RAM usage went up. I have thus decided to focus a bit on=
the issue of const-correctness, in an attempt to contain the areas that ca=
n do damage in a multithreaded environment.<br><br>One of the tentacles of =
this octopus that I'm working on is a scientific data analysis package that=
starts from a 3D point cloud and attempts to extract helicoidal features f=
rom it, in a fashion that is tolerant to both false positives and negatives=
in the input dataset. The point cloud is located on a fairly complex model=
of the detector, which in software is fully implemented using class hierar=
chies. You have a class hierarchy for volumes, a class hierarchy for materi=
als, a class hierarchy for surfaces, a class hierarchy for layers (mostly a=
group of volumes with a bounding box), and so on, and all these classes cr=
oss-reference one another using pointer-to-base-class, sometimes raw (e.g. =
Surface*), sometimes owning (e.g. shared_ptr<Surface>).<br><br>If I t=
ake the Surface class hierarchy as an example, a quick search for "Surface*=
", "shared_ptr<Surface>" and "shared_ptr<const Surface>", where=
Surface is the base class of all concrete surface classes, leads a couple =
of hundreds of results. Several of these reside in public interfaces, both =
as inputs and as outputs, and changing them could have broader implication =
for clients that I'm not aware of. Outputs are not a problem: I can just ta=
ke a pointer to the contents of polymorphic value. But inputs, as far as I =
can tell, are a much bigger issue: if a client ever gives us a Surface*, we=
ll, polymorphic_value will not be able to help us.<br><br>This is just one =
example of a single class hierarchy, in a single package of a big codebase.=
And it's far from the worst package that we have, people have already spen=
t quite a lot of time cleaning it up. But it does not make me very optimist=
ic about the viability of migrating to polymorphic_value in this kind of si=
tuation.<br><br> </div><blockquote class=3D"gmail_quote" style=3D"marg=
in: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><d=
iv dir=3D"auto"><blockquote type=3D"cite"><div><div dir=3D"ltr"><div>Once y=
ou introduce a polymorphic_value somewhere, you also need to rewrite every =
other piece of client code that uses the same class hierarchy, often includ=
ing third-party libraries that you may have no easy access to. Even though =
the final code will be cleaner, the process will be even more costly as add=
ing clone() methods everywhere. And for large projects, that will just not =
be seen as a viable endeavour.<br><br></div></div></div></blockquote><div><=
br></div><div>I've not found this to be the case though, like many things, =
it probably depends on patterns at use in the code.</div><br><blockquote ty=
pe=3D"cite"><div><div dir=3D"ltr"><div>I believe that with the modification=
s that I am proposing, propagate_const would be a better fit for such refac=
toring purposes. With it, one could progressively introduce const-correct p=
ointer manipulation at relatively little cost. Sure, you wouldn't have deep=
copies, but hey, developers of existing codebases never had them to begin =
with. So we aren't going to miss them in a context of progressive code evol=
ution. And if error-prone shallow copies are harder to perform by accident,=
well, all the better.<br><br> <br><br></div><blockquote class=3D"gmai=
l_quote" style=3D"margin:0;margin-left:0.8ex;border-left:1px #ccc solid;pad=
ding-left:1ex"><div dir=3D"ltr"><div><div class=3D"gmail_quote"><blockquote=
class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left-width:=
1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left=
:1ex"><div dir=3D"ltr"><br><div style=3D"background-color:rgb(250,250,250);=
border:1px solid rgb(187,187,187)"><code><div><span style=3D"color:rgb(0,0,=
136)">class</span><span style=3D"color:rgb(0,0,0)"> </span><span style=3D"c=
olor:rgb(102,0,102)">Interface</span><span style=3D"color:rgb(0,0,0)"><br><=
/span><span style=3D"color:rgb(102,102,0)">{</span><span style=3D"color:rgb=
(0,0,0)"><br></span><span style=3D"color:rgb(0,0,136)">public</span><span s=
tyle=3D"color:rgb(102,102,0)">:</span><span style=3D"color:rgb(0,0,0)"><br>=
</span><span style=3D"color:rgb(136,0,0)">// This kind of boilerplat=
e must be added to every class supporting deep copy...</span><span style=3D=
"color:rgb(0,0,0)"><br> </span><span style=3D"color:rgb(0,0,136)">vir=
tual</span><span style=3D"color:rgb(0,0,0)"> std</span><span style=3D"color=
:rgb(102,102,0)">::</span><span style=3D"color:rgb(0,0,0)">unique_ptr</span=
><span style=3D"color:rgb(102,102,0)"><</span><span style=3D"color:rgb(1=
02,0,102)">Interface</span><span style=3D"color:rgb(102,102,0)">></span>=
<span style=3D"color:rgb(0,0,0)"> clone</span><span style=3D"color:rgb(102,=
102,0)">()</span><span style=3D"color:rgb(0,0,0)"> </span><span style=3D"co=
lor:rgb(0,0,136)">const</span><span style=3D"color:rgb(0,0,0)"> </span><spa=
n style=3D"color:rgb(102,102,0)">=3D</span><span style=3D"color:rgb(0,0,0)"=
> </span><span style=3D"color:rgb(0,102,102)">0</span><span style=3D"color:=
rgb(102,102,0)">;</span><span style=3D"color:rgb(0,0,0)"><br></span><span s=
tyle=3D"color:rgb(102,102,0)">};</span><span style=3D"color:rgb(0,0,0)"><br=
><br></span><span style=3D"color:rgb(0,0,136)">class</span><span style=3D"c=
olor:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,0,102)">Implementatio=
n</span><span style=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(1=
02,102,0)">:</span><span style=3D"color:rgb(0,0,0)"> </span><span style=3D"=
color:rgb(0,0,136)">public</span><span style=3D"color:rgb(0,0,0)"> </span><=
span style=3D"color:rgb(102,0,102)">Interface</span><span style=3D"color:rg=
b(0,0,0)"><br></span><span style=3D"color:rgb(102,102,0)">{</span><span sty=
le=3D"color:rgb(0,0,0)"><br></span><span style=3D"color:rgb(0,0,136)">publi=
c</span><span style=3D"color:rgb(102,102,0)">:</span><span style=3D"color:r=
gb(0,0,0)"><br> </span><span style=3D"color:rgb(136,0,0)">// ...and r=
eplicated again and again in every single non-abstract child. No fun.</span=
><span style=3D"color:rgb(0,0,0)"><br> std</span><span style=3D"color=
:rgb(102,102,0)">::</span><span style=3D"color:rgb(0,0,0)">unique_ptr</span=
><span style=3D"color:rgb(102,102,0)"><</span><span style=3D"color:rgb(1=
02,0,102)">Interface</span><span style=3D"color:rgb(102,102,0)">></span>=
<span style=3D"color:rgb(0,0,0)"> clone</span><span style=3D"color:rgb(102,=
102,0)">()</span><span style=3D"color:rgb(0,0,0)"> </span><span style=3D"co=
lor:rgb(0,0,136)">const</span><span style=3D"color:rgb(0,0,0)"> </span><spa=
n style=3D"color:rgb(0,0,136)">final</span><span style=3D"color:rgb(0,0,0)"=
> </span><span style=3D"color:rgb(0,0,136)">override</span><span style=3D"c=
olor:rgb(0,0,0)"><br> </span><span style=3D"color:rgb(102,102,0)">{</=
span><span style=3D"color:rgb(0,0,0)"><br> </span><span style=
=3D"color:rgb(0,0,136)">return</span><span style=3D"color:rgb(0,0,0)"> std<=
/span><span style=3D"color:rgb(102,102,0)">::</span><span style=3D"color:rg=
b(0,0,0)">make_unique</span><span style=3D"color:rgb(102,102,0)"><</span=
><span style=3D"color:rgb(102,0,102)">Implementatio<wbr>n</span><span style=
=3D"color:rgb(102,102,0)">>(</span><span style=3D"color:rgb(0,0,0)"> </s=
pan><span style=3D"color:rgb(102,102,0)">*</span><span style=3D"color:rgb(0=
,0,136)">this</span><span style=3D"color:rgb(0,0,0)"> </span><span style=3D=
"color:rgb(102,102,0)">);</span><span style=3D"color:rgb(0,0,0)"><br> =
</span><span style=3D"color:rgb(102,102,0)">}</span><span style=3D"color:r=
gb(0,0,0)"><br></span><span style=3D"color:rgb(102,102,0)">};</span></div><=
/code></div><br>But thankfully, sometimes, we can do without a true deep co=
py, and satisfy ourselves with a shallow copy. And in fact, for some use ca=
ses, a shallow copy will even be exactly what we want:<br><ul><li>When buil=
ding some variety of search acceleration structure (hashmap, neighbour loca=
tor, axis-aligned-bounding box...)</li><li>When many "slave objects" share =
a reference to some common, potentially large "master object"</li></ul>Unfo=
rtunately, even though all standard C++ pointer types make it trivial to sh=
allow-copy a polymorphic object, propagate_const gets in our way here by be=
ing move only. I see this design choice as a shortcoming, because it reduce=
s the usefulness of propagate_const in many dynamic polymorphism use cases =
where the underlying pointer type would have done just fine. For example, w=
ithout shallow copy operations...<br><ul><li>You cannot let the compiler im=
plement a shallow copy constructor for you using "=3D default"<br></li><li>=
You cannot use std::copy_if to find objects matching some predicate in an i=
nternal dataset</li><li>You cannot easily build object search acceleration =
structures, such as hashmaps or AABBs</li><li>You cannot easily share a ref=
erence to an object wrapped by propagate_const with other objects</li></ul>=
<p>I am aware that shallow copies are possible using the get() and get_unde=
rlying() operations. I do not see this as a satisfactory answer, because it=
breaks every STL-ish algorithm that expects a copy constructor. Of course,=
I could build a propagate_const wrapper that has a copy constructor myself=
, and this is what I would end up doing if propagate_const were accepted in=
the STL in its current form. But I think that the use cases that I mention=
ed above are valid enough to warrant changing the design of propagate_const=
instead.</p></div></blockquote><blockquote class=3D"gmail_quote" style=3D"=
margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,20=
4,204);border-left-style:solid;padding-left:1ex"><div dir=3D"ltr"><p></p><p=
>I am aware that in order to be const-correct, a shallow copy constructor f=
or propagate_const would need to operate from a non-const reference. I am f=
ine with this tradeoff. The main reason we usually allow ourselves to make =
mutable copies from const objects is because we assume the copy to be indep=
endent from the original object. This assumption is broken when making shal=
low copies through pointer or reference types. Sure, many APIs will be brok=
en initially, but that is unavoidable when fixing old programming language =
flaws. Bugs will be reported, and interfaces will be fixed, the way it's al=
ways been done.<br></p><p><br></p><p>For prior art, if you look at the STL'=
s documentation, you will find that customizable algorithms that copy const=
data, such as std::copy_if, explicitly do NOT require the user-provided fu=
nctions to consume the data by const-reference. So the idea of copying from=
non-const is not new. The C++ community only needs to re-discover it.<br><=
/p><p><br></p></div></blockquote><div><br></div><div>We considered making c=
opies from non-const propagate_const legal but were strongly dissuaded by 3=
major standard library implementers.</div></div></div></div></blockquote><=
div><br>Sure, they disagreed. But what were their argument for it? Was it j=
ust a case of "the maffia said no"?<br><br> </div><blockquote class=3D=
"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-left:1px #ccc soli=
d;padding-left:1ex"><div dir=3D"ltr"><div><div class=3D"gmail_quote"><div>&=
nbsp; </div><blockquote class=3D"gmail_quote" style=3D"margin:0px 0px =
0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-l=
eft-style:solid;padding-left:1ex"><div dir=3D"ltr"><p></p><p>I am also awar=
e of the objection, made in a thread linked above, that "We were not confid=
ent that a non-const copy constructor would protect=20
the user from accidental loss of const and accidental mutable shared=20
state". As far as I'm concerned, this hand-waving statement was not properl=
y justified. From my perspective, the proposed alternative of using get_und=
erlying seems to be much bigger breach of const-correctness than a copy con=
structor that operates from mutable, because from a semantic point of view,=
get_underlying is essentially a silent const_cast that standard code analy=
sis tools won't catch.</p><p><br></p></div></blockquote><div><br></div><div=
>We added `get_underlying` as an nicer alternative to `reinterpret_cast`, w=
hich would give access to the underlying pointer.<br></div><div><br></div><=
div>Our design sought to avoid silent surprise. Granted a user can do the w=
rong thing with get_underlying but the code is written by the user, not by =
the compiler. Forcing the user to be explicit offers some protection.</div>=
</div></div></div></blockquote><div><br>Been there, done that. When you see=
all the tools that C++ and its standard library provide to help you shoot =
yourself in the foot, it is always very tempting to roll your own, thinking=
that surely it will do the job better. Except usually, it doesn't. When wh=
at you want is a cast, make it a cast, don't try to make it look nicer by b=
urying it in custom abstractions.<br><br>Let's take a step back and think a=
bout this from an API design point of view. Why makes get_underlying such a=
n poor interface?<br><br>1/ By returning a reference to the internal pointe=
r object, you leak implementation details<br>2/ By leaking implementation d=
etails, you lose the ability to maintain the single most important class in=
variant of propagate_const, which is that type-safe accesses to a const pro=
pagate_const wrapper should not give one write access to the internal objec=
t.<br><br>Now, I am aware that we are discussing C++ API design here. Leaki=
ng implementation details and violating class invariants is okay, as long a=
s it can give me that 0.1% performance improvement in some very obscure use=
case that someone thought about someday. One a single compiler. With the r=
ight combination of flags. Until the next release. Perhaps.<br><br>But in t=
he case of get_underlying, you could actually leak these implementation det=
ails, if you really wanted to, without violating your class invariants alon=
g the way. The fix is simple, really. Make get_underlying a non-const metho=
d of the propagate_const object, and drop get_underlying from const. Be ser=
ious about interface guarantees.<br><br>If your users come whining because =
they can't easily extract a pointer-to-mutable from a const object, try to =
give them the talk about const correctness. If they won't listen, tell them=
to const_cast the propagate_const wrapper. But don't compromise your API j=
ust to make stupid operations more convenient to write. It is never worth i=
t.<br><br><br> <br></div><blockquote class=3D"gmail_quote" style=3D"ma=
rgin:0;margin-left:0.8ex;border-left:1px #ccc solid;padding-left:1ex"><div =
dir=3D"ltr"><div><div class=3D"gmail_quote"><blockquote class=3D"gmail_quot=
e" style=3D"margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-colo=
r:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div dir=3D"lt=
r"><p></p><br><font size=3D"4">Const-correct convertibility to the underlyi=
ng pointer type</font><br><br>Sometimes, one has to extract a pointer back =
from the propagate_const wrapper. I would see two main use cases for this:<=
br><ul><li>To feed a legacy API that is not (yet?) compatible with propagat=
e_const.<br></li><li>To turn a propagate_const<T> into a pointer-to-c=
onst, when the recipient should not be able to modify to the target object<=
/li></ul><p>The current propagate_const interface provides three ways to do=
this:</p><ul><li>Call get() and get a raw pointer</li><li>Rely on an impli=
cit cast to raw pointer, if available</li><li>Use get_underlying to access =
the internals of propagate_const.</li></ul><p>As I'm going to elaborate, th=
e first two operations are not always the right tool for the job at hand, b=
ecause they fail to convey important ownership information from the underly=
ing pointer type. Whereas get_underlying, as currently implemented, is a fl=
awed interface that goes directly against the design goals of propagate_con=
st and should be eradicated before standardization.</p><p><br></p><p>As a u=
se case, suppose that as discussed previously, I am working a "slave object=
", which holds a shared_ptr to an associated "master object" that it shares=
with other slaves. To improve the const-correctness of this design, I have=
decided to refactor the shared_ptr into a propagate_const<shared_ptr>=
;. So far, so good.<br></p><p><br></p><p></p><div style=3D"background-color=
:rgb(250,250,250);border:1px solid rgb(187,187,187)"><code><div><span style=
=3D"color:rgb(0,0,136)">class</span><span style=3D"color:rgb(0,0,0)"> </spa=
n><span style=3D"color:rgb(102,0,102)">Master</span><span style=3D"color:rg=
b(0,0,0)"><br></span><span style=3D"color:rgb(102,102,0)">{</span><span sty=
le=3D"color:rgb(0,0,0)"><br> </span><span style=3D"color:rgb(102,102,=
0)">...</span><span style=3D"color:rgb(0,0,0)"><br></span><span style=3D"co=
lor:rgb(102,102,0)">};</span><span style=3D"color:rgb(0,0,0)"><br><br></spa=
n><span style=3D"color:rgb(0,0,136)">class</span><span style=3D"color:rgb(0=
,0,0)"> </span><span style=3D"color:rgb(102,0,102)">Slave</span><span style=
=3D"color:rgb(0,0,0)"><br></span><span style=3D"color:rgb(102,102,0)">{</sp=
an><span style=3D"color:rgb(0,0,0)"><br></span><span style=3D"color:rgb(0,0=
,136)">private</span><span style=3D"color:rgb(102,102,0)">:</span><span sty=
le=3D"color:rgb(0,0,0)"><br> </span><span style=3D"color:rgb(136,0,0)=
">// This was refactored from "std::shared_ptr<Master> m_master"</spa=
n><span style=3D"color:rgb(0,0,0)"><br> std</span><span style=3D"colo=
r:rgb(102,102,0)">::</span><span style=3D"color:rgb(0,0,0)">propagate_const=
</span><span style=3D"color:rgb(102,102,0)"><</span><span style=3D"color=
:rgb(0,0,0)">std</span><span style=3D"color:rgb(102,102,0)">::</span><span =
style=3D"color:rgb(0,0,0)">shar<wbr>ed_ptr</span><span style=3D"color:rgb(1=
02,102,0)"><</span><span style=3D"color:rgb(102,0,102)">Master</span><sp=
an style=3D"color:rgb(102,102,0)">>></span><span style=3D"color:rgb(0=
,0,0)"> m_master</span><span style=3D"color:rgb(102,102,0)">;</span><span s=
tyle=3D"color:rgb(0,0,0)"><br><br></span><span style=3D"color:rgb(0,0,136)"=
>public</span><span style=3D"color:rgb(102,102,0)">:</span><span style=3D"c=
olor:rgb(0,0,0)"><br> </span><span style=3D"color:rgb(102,0,102)">Sla=
ve</span><span style=3D"color:rgb(102,102,0)">(</span><span style=3D"color:=
rgb(0,0,0)"> std</span><span style=3D"color:rgb(102,102,0)">::</span><span =
style=3D"color:rgb(0,0,0)">shared_ptr</span><span style=3D"color:rgb(102,10=
2,0)"><</span><span style=3D"color:rgb(102,0,102)">Master</span><span st=
yle=3D"color:rgb(102,102,0)">></span><span style=3D"color:rgb(0,0,0)"> m=
aster </span><span style=3D"color:rgb(102,102,0)">)</span><span style=3D"co=
lor:rgb(0,0,0)"><br> </span><span style=3D"color:rgb(102,102,0=
)">:</span><span style=3D"color:rgb(0,0,0)"> m_master</span><span style=3D"=
color:rgb(102,102,0)">(</span><span style=3D"color:rgb(0,0,0)">master</span=
><span style=3D"color:rgb(102,102,0)">)</span><span style=3D"color:rgb(0,0,=
0)"><br> </span><span style=3D"color:rgb(102,102,0)">{</span><span st=
yle=3D"color:rgb(0,0,0)"><br> m_master</span><span style=3D"co=
lor:rgb(102,102,0)">-></span><span style=3D"color:rgb(0,0,0)">addSlave</=
span><span style=3D"color:rgb(102,102,0)">(</span><span style=3D"color:rgb(=
0,0,0)"> </span><span style=3D"color:rgb(102,102,0)">*</span><span style=3D=
"color:rgb(0,0,136)">this</span><span style=3D"color:rgb(0,0,0)"> </span><s=
pan style=3D"color:rgb(102,102,0)">);</span><span style=3D"color:rgb(0,0,0)=
"><br> </span><span style=3D"color:rgb(102,102,0)">}</span><span styl=
e=3D"color:rgb(0,0,0)"><br><br> ...<br></span><span style=3D"color:rg=
b(102,102,0)">};</span><span style=3D"font-family:arial,sans-serif;backgrou=
nd-color:rgb(255,255,255)"> </span></div></code></div></div></blockquo=
te><blockquote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;bord=
er-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:soli=
d;padding-left:1ex"><div dir=3D"ltr"><p></p>But as I proceed with the refac=
toring, I discover that the Slave class used to provide a method that share=
s access to its master:<br><br><div style=3D"background-color:rgb(250,250,2=
50);border:1px solid rgb(187,187,187)"><code><div><span style=3D"color:rgb(=
0,0,0)">std</span><span style=3D"color:rgb(102,102,0)">::</span><span style=
=3D"color:rgb(0,0,0)">shared_ptr</span><span style=3D"color:rgb(102,102,0)"=
><</span><span style=3D"color:rgb(102,0,102)">Master</span><span style=
=3D"color:rgb(102,102,0)">></span><span style=3D"color:rgb(0,0,0)"> </sp=
an><span style=3D"color:rgb(102,0,102)">Slave</span><span style=3D"color:rg=
b(102,102,0)">::</span><span style=3D"color:rgb(0,0,0)">getMaster</span><sp=
an style=3D"color:rgb(102,102,0)">()</span><span style=3D"color:rgb(0,0,0)"=
> </span><span style=3D"color:rgb(0,0,136)">const</span><span style=3D"colo=
r:rgb(0,0,0)"><br></span><span style=3D"color:rgb(102,102,0)">{</span><span=
style=3D"color:rgb(0,0,0)"><br> </span><span style=3D"color:rgb(0,0,=
136)">return</span><span style=3D"color:rgb(0,0,0)"> m_master</span><span s=
tyle=3D"color:rgb(102,102,0)">;</span><span style=3D"color:rgb(0,0,0)"><br>=
</span><span style=3D"color:rgb(102,102,0)">}</span></div></code></div><br>=
This does not compile anymore. And I'm happy about that: it should never ha=
ve compiled to begin with. Returning non-const access to my members from a =
const method definitely does not match my idea of const-correctness!<br><br=
>Instead, I would like to only provide const access to the master object, l=
ike so:<br><br><div style=3D"background-color:rgb(250,250,250);border:1px s=
olid rgb(187,187,187)"><code><div><span style=3D"color:rgb(0,0,0)">std</spa=
n><span style=3D"color:rgb(102,102,0)">::</span><span style=3D"color:rgb(0,=
0,0)">shared_ptr</span><span style=3D"color:rgb(102,102,0)"><</span><spa=
n style=3D"color:rgb(0,0,136)">const</span><span style=3D"color:rgb(0,0,0)"=
> </span><span style=3D"color:rgb(102,0,102)">Master</span><span style=3D"c=
olor:rgb(102,102,0)">></span><span style=3D"color:rgb(0,0,0)"> </span><s=
pan style=3D"color:rgb(102,0,102)">Slave</span><span style=3D"color:rgb(102=
,102,0)">::</span><span style=3D"color:rgb(0,0,0)">getMaster</span><span st=
yle=3D"color:rgb(102,102,0)">()</span><span style=3D"color:rgb(0,0,0)"> </s=
pan><span style=3D"color:rgb(0,0,136)">const</span><span style=3D"color:rgb=
(102,102,0)">;</span></div></code></div><br>If my clients are well-behaved =
and do not mutate the master object, this will be a minimally invasive inte=
rface change. It will require only minor client rewrites, the kind that cou=
ld be automated by sed or an IDE. It could even require no client rewrite a=
t all if I end up being lucky and have clients that were using auto and fri=
ends.<br><br>Unfortunately, the current propagate_const interface does not =
allow me to implement this method in a clean way.<br><br></div></blockquote=
><div><br></div><div>Sadly that's intended. We can't guarantee that "client=
s are well-behaved and do not mutate the master object".</div></div></div><=
/div></blockquote><div><br>And you do not need to, that's my job. You give =
me a well-designed const-correct pointer wrapper, and I'll use it to improv=
e the const-correctness of the codebase that I'm responsible of. If I find =
broken client code, I will do my best to fix it myself, and send an angry e=
-mail to the person responsible if I don't manage. It's called maintenance.=
<br><br>But when I'm going through the pain of doing this, I would like to =
do so using quality tools that give me the impression that the effort is wo=
rthwhile, and won't be undermined on the day after by someone trying to do =
something stupid, going through the STL documentation and thinking "oh, I k=
now, I'll just use get_underlying...".<br><br>When people start to break th=
e thread-safety of our code by introducing mutation in places where it does=
n't belong, I want them to explicitly write down the dreaded word "cast" an=
d feel that chill going down their spine, telling them that they're doing s=
omething wrong. I want them to figure out that the basic design of their co=
de is broken, and to understand that they need to fix it before submitting =
it upstream. I don't want the only line of defense against software chaos t=
o be me telling them at the end, when they finally submit their merge reque=
st, that I can't accept it into our codebase and they need to rewrite half =
of it.<br><br>Good interfaces save everyone's time by making bad things loo=
k bad. Let's have more of them in C++.<br><br> </div><blockquote class=
=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-left:1px #ccc s=
olid;padding-left:1ex"><div dir=3D"ltr"><div><div class=3D"gmail_quote"><di=
v> </div><blockquote class=3D"gmail_quote" style=3D"margin:0px 0px 0px=
0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left=
-style:solid;padding-left:1ex"><div dir=3D"ltr">The obvious code snippet wo=
uld not work due to the lack of an appropriate implicit conversion:<br><br>=
<div style=3D"background-color:rgb(250,250,250);border:1px solid rgb(187,18=
7,187)"><code><div><span style=3D"color:rgb(0,0,0)">std</span><span style=
=3D"color:rgb(102,102,0)">::</span><span style=3D"color:rgb(0,0,0)">shared_=
ptr</span><span style=3D"color:rgb(102,102,0)"><const </span><span style=
=3D"color:rgb(102,0,102)">Master</span><span style=3D"color:rgb(102,102,0)"=
>></span><span style=3D"color:rgb(0,0,0)"> </span><span style=3D"color:r=
gb(102,0,102)">Slave</span><span style=3D"color:rgb(102,102,0)">::</span><s=
pan style=3D"color:rgb(0,0,0)">getMaster</span><span style=3D"color:rgb(102=
,102,0)">()</span><span style=3D"color:rgb(0,0,0)"> </span><span style=3D"c=
olor:rgb(0,0,136)">const</span><span style=3D"color:rgb(0,0,0)"><br></span>=
<span style=3D"color:rgb(102,102,0)">{</span><span style=3D"color:rgb(0,0,0=
)"><br> </span><span style=3D"color:rgb(0,0,136)">return</span><span =
style=3D"color:rgb(0,0,0)"> m_master</span><span style=3D"color:rgb(102,102=
,0)">;</span><span style=3D"color:rgb(0,0,0)"> // ERROR: No implicit =
conversion from propagate_const<shared_ptr>!<br></span><span style=3D=
"color:rgb(102,102,0)">}</span></div></code></div><br>Using get() here woul=
d be the perfect way to introduce use after free bugs:<br><br><div style=3D=
"background-color:rgb(250,250,250);border:1px solid rgb(187,187,187)"><code=
><div><span style=3D"color:rgb(0,0,0)">std</span><span style=3D"color:rgb(1=
02,102,0)">::</span><span style=3D"color:rgb(0,0,0)">shared_ptr</span><span=
style=3D"color:rgb(102,102,0)"><const </span><span style=3D"color:rgb(1=
02,0,102)">Master</span><span style=3D"color:rgb(102,102,0)">></span><sp=
an style=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,0,102)">=
Slave</span><span style=3D"color:rgb(102,102,0)">::</span><span style=3D"co=
lor:rgb(0,0,0)">getMaster</span><span style=3D"color:rgb(102,102,0)">()</sp=
an><span style=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(0,0,13=
6)">const</span><span style=3D"color:rgb(0,0,0)"><br></span><span style=3D"=
color:rgb(102,102,0)">{</span><span style=3D"color:rgb(0,0,0)"><br> c=
onst Master* master =3D m_master.get();<br> </span><span style=3D"col=
or:rgb(0,0,136)">return</span><span style=3D"color:rgb(0,0,0)"> std::shared=
_ptr<Master>( master</span><span style=3D"color:rgb(102,102,0)"> );</=
span><span style=3D"color:rgb(0,0,0)"> // INCORRECT: Two owners for o=
ne object!<br></span><span style=3D"color:rgb(102,102,0)">}</span></div></c=
ode></div><br>And using get_underlying will both break const correctness an=
d fail to provide me with the correct return type:<br><br><div style=3D"bac=
kground-color:rgb(250,250,250);border:1px solid rgb(187,187,187)"><code><di=
v><span style=3D"color:rgb(0,0,0)">std</span><span style=3D"color:rgb(102,1=
02,0)">::</span><span style=3D"color:rgb(0,0,0)">shared_ptr</span><span sty=
le=3D"color:rgb(102,102,0)"><const </span><span style=3D"color:rgb(102,0=
,102)">Master</span><span style=3D"color:rgb(102,102,0)">></span><span s=
tyle=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,0,102)">Slav=
e</span><span style=3D"color:rgb(102,102,0)">::</span><span style=3D"color:=
rgb(0,0,0)">getMaster</span><span style=3D"color:rgb(102,102,0)">()</span><=
span style=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(0,0,136)">=
const</span><span style=3D"color:rgb(0,0,0)"><br></span><span style=3D"colo=
r:rgb(102,102,0)">{</span><span style=3D"color:rgb(0,0,0)"><br> auto =
master =3D std::get_underlying( m_master ); // This is an std::shared=
_ptr<Master><br> </span><span style=3D"color:rgb(0,0,136)">retu=
rn</span><span style=3D"color:rgb(0,0,0)"> master; // ERROR: No impli=
cit conversion to std::shared_ptr<const Master>!</span><span style=3D=
"color:rgb(0,0,0)"><br></span><span style=3D"color:rgb(102,102,0)">}</span>=
</div></code></div><br>To implement this method, in addition to the const-i=
ncorrect get_underlying interface, I would also need a const cast!<br><br><=
div style=3D"background-color:rgb(250,250,250);border:1px solid rgb(187,187=
,187)"><code><div><span style=3D"color:rgb(0,0,0)">std</span><span style=3D=
"color:rgb(102,102,0)">::</span><span style=3D"color:rgb(0,0,0)">shared_ptr=
</span><span style=3D"color:rgb(102,102,0)"><const </span><span style=3D=
"color:rgb(102,0,102)">Master</span><span style=3D"color:rgb(102,102,0)">&g=
t;</span><span style=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(=
102,0,102)">Slave</span><span style=3D"color:rgb(102,102,0)">::</span><span=
style=3D"color:rgb(0,0,0)">getMaster</span><span style=3D"color:rgb(102,10=
2,0)">()</span><span style=3D"color:rgb(0,0,0)"> </span><span style=3D"colo=
r:rgb(0,0,136)">const</span><span style=3D"color:rgb(0,0,0)"><br></span><sp=
an style=3D"color:rgb(102,102,0)">{</span><span style=3D"color:rgb(0,0,0)">=
<br> auto master =3D std::get_underlying( m_master ); <br> </sp=
an><span style=3D"color:rgb(0,0,136)">return</span><span style=3D"color:rgb=
(0,0,0)"> std::const_pointer_cast<const Master>( master );</span><spa=
n style=3D"color:rgb(0,0,0)"><br></span><span style=3D"color:rgb(102,102,0)=
">}</span></div></code></div><p><br></p><p>For sure, I would never want an =
abomination like this to pass code review :)</p><p><br></p></div></blockquo=
te><div>Agreed.</div><div> </div><blockquote class=3D"gmail_quote" sty=
le=3D"margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(=
204,204,204);border-left-style:solid;padding-left:1ex"><div dir=3D"ltr"><p>=
</p><p>This little thought experiment showcases two major issues with get_u=
nderlying:</p><ul><li>Counter to the goal of of propagate_const, it makes i=
t trivial to violate const-correctness without the compiler noticing (or, f=
or that matter, any developer or static analysis tool that is unfamiliar wi=
th the propagate_const API).<br></li><li>By not returning the const-correct=
type, get_underlying can make it unnecessarily hard to implement const-cor=
rect interfaces.<br></li></ul><p></p><p>The authors of the original propaga=
te_const proposal were aware that the get_underlying interface was suboptim=
al, and for this reason they opted to make it less usable by hiding it as a=
free function instead of making it a proper method of propagate_const. But=
personnally, I would go further: drop get_underlying altogether, and repla=
ce it with something that is both const-correct and respectful of pointer o=
wnership issues.<br></p><p><br></p><p>My counter-proposal would be to have =
a propagate_const method, maybe called "share()", which enables sharing acc=
ess to the data pointed by propagate_const with due respect paid to both co=
nst-correctness and the underlying pointer's ownership semantics. In practi=
ce:</p><ul><li>If called on propagate_const<T*>, share() should retur=
n a T*</li><li>If called on const propagate_const<T*>, share() should=
return a const T*</li><li>If called on propagate_const<shared_ptr<T&=
gt;><wbr>, share() should return a shared_ptr<T></li><li>If called=
on const propagate_const<shared_ptr<T>><wbr>, share() should r=
eturn a shared_ptr<const T></li><li>share() should not be defined on =
propagate_const<unique_ptr<T>> since that pointer does not supp=
ort sharing ownership</li></ul><p>In addition, as a convenience, whenever a=
share() method exists, an implicit cast could be provided to the underlyin=
g pointer type as a way to make such ownership sharing more convenient and =
to enable things like comparison of containers of shared_ptr<T> with =
containers of propagate_const<shared_ptr<T>> through standard (=
STL-ish) algorithms.</p><p><br></p><p>If you really, positively want someth=
ing like get_underlying to exist, it should only be applicable to non-const=
object. This alone would make it const-correct, and thus remove the need t=
o implement it as a free function and be careful when using it.</p><p><br><=
/p></div></blockquote></div></div></div></blockquote></div></div></blockquo=
te><div>This is a pretty convincing argument.</div><div>Perhaps a paper for=
Toronto?</div><div>Let me know if you'd like any input.</div></div></block=
quote><div><br>Well, to begin with, I do not completely understand what you=
are looking for. Would you like me to write some sort of LaTeX article des=
cribing the proposals that I've outlined here in a more formal way?<br><br>=
</div></div></div></blockquote><div><br></div><div><span style=3D"bac=
kground-color: rgba(255, 255, 255, 0);">Having thought a bit more about you=
r issu</span>e I'm intending to write something myself and will put your na=
me on it. I'll contact you off-list.</div><br><blockquote type=3D"cite"><di=
v><div dir=3D"ltr"><blockquote class=3D"gmail_quote" style=3D"margin: 0;mar=
gin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D=
"auto"><div></div><blockquote type=3D"cite"><div><div dir=3D"ltr"><blockquo=
te class=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-left:1p=
x #ccc solid;padding-left:1ex"><div dir=3D"ltr"><div><div class=3D"gmail_qu=
ote"><blockquote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;bo=
rder-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:so=
lid;padding-left:1ex"><div dir=3D"ltr"><p>With this simple change, shameles=
s people who want to get their hands dirty and extract a mutable pointer fr=
om a const wrapper will then need to do some extra work, as should be expec=
ted of them in my opinion:</p><p><br></p><div style=3D"background-color:rgb=
(250,250,250);border:1px solid rgb(187,187,187)"><code><div><span style=3D"=
color:rgb(0,0,0)"></span><span style=3D"color:rgb(0,0,136)">using</span><sp=
an style=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(102,0,102)">=
ConstPropagator</span><span style=3D"color:rgb(0,0,0)"> </span><span style=
=3D"color:rgb(102,102,0)">=3D</span><span style=3D"color:rgb(0,0,0)"> std</=
span><span style=3D"color:rgb(102,102,0)">::</span><span style=3D"color:rgb=
(0,0,0)">propagate_const</span><span style=3D"color:rgb(102,102,0)"><</s=
pan><span style=3D"color:rgb(0,0,0)">std</span><span style=3D"color:rgb(102=
,102,0)">::</span><span style=3D"color:rgb(0,0,0)">shar<wbr>ed_ptr</span><s=
pan style=3D"color:rgb(102,102,0)"><</span><span style=3D"color:rgb(0,0,=
0)">T</span><span style=3D"color:rgb(102,102,0)">>>;</span><span styl=
e=3D"color:rgb(0,0,0)"><br><br></span><span style=3D"color:rgb(0,0,136)">co=
nst</span><span style=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb=
(102,0,102)">ConstPropagator</span><span style=3D"color:rgb(102,102,0)">&am=
p;</span><span style=3D"color:rgb(0,0,0)"> immutableRef </span><span style=
=3D"color:rgb(102,102,0)">=3D</span><span style=3D"color:rgb(0,0,0)"> </spa=
n><span style=3D"color:rgb(102,102,0)">...</span><span style=3D"color:rgb(0=
,0,0)"> </span><span style=3D"color:rgb(102,102,0)">;</span><span style=3D"=
color:rgb(0,0,0)"><br>std</span><span style=3D"color:rgb(102,102,0)">::</sp=
an><span style=3D"color:rgb(0,0,0)">shared_ptr</span><span style=3D"color:r=
gb(102,102,0)"><</span><span style=3D"color:rgb(0,0,0)">T</span><span st=
yle=3D"color:rgb(102,102,0)">></span><span style=3D"color:rgb(0,0,0)"> m=
utableRef </span><span style=3D"color:rgb(102,102,0)">=3D</span><span style=
=3D"color:rgb(0,0,0)"> </span><span style=3D"color:rgb(0,0,136)">const_cast=
</span><span style=3D"color:rgb(102,102,0)"><</span><span style=3D"color=
:rgb(102,0,102)">ConstPropagator</span><span style=3D"color:rgb(102,102,0)"=
>*>(</span><span style=3D"color:rgb(0,0,0)"> </span><span style=3D"color=
:rgb(102,102,0)">&</span><span style=3D"color:rgb(0,0,0)">immutableRef =
</span><span style=3D"color:rgb(102,102,0)">)-></span><span style=3D"col=
or:rgb(0,0,0)">get_underlying</span><span style=3D"color:rgb(102,102,0)">()=
;</span></div></code></div></div><span><font color=3D"#888888">
<p></p>
-- <br>
You received this message because you are subscribed to the Google Groups "=
ISO C++ Standard - Future Proposals" group.<br>
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a rel=3D"nofollow">std-proposal...@isocpp.org</a>.<br>
To post to this group, send email to <a rel=3D"nofollow">std-pr...@isocpp.o=
rg</a>.<br>
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/9ad3f22a-a619-4488-bf1e-303641c968b9%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter" rel=3D"nofollow" t=
arget=3D"_blank" onmousedown=3D"this.href=3D'https://groups.google.com/a/is=
ocpp.org/d/msgid/std-proposals/9ad3f22a-a619-4488-bf1e-303641c968b9%40isocp=
p.org?utm_medium\x3demail\x26utm_source\x3dfooter';return true;" onclick=3D=
"this.href=3D'https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/=
9ad3f22a-a619-4488-bf1e-303641c968b9%40isocpp.org?utm_medium\x3demail\x26ut=
m_source\x3dfooter';return true;">https://groups.google.com/a/<wbr>isocpp.o=
rg/d/msgid/std-<wbr>proposals/9ad3f22a-a619-4488-<wbr>bf1e-303641c968b9%40i=
socpp.org</a><wbr>.<br>
</font></span></blockquote></div><br></div></div>
</blockquote></div>
<p></p>
-- <br>
You received this message because you are subscribed to the Google Groups "=
ISO C++ Standard - Future Proposals" group.<br>
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"javascript:" target=3D"_blank" gdf-obfuscated-mailto=3D"=
NhQZldkPAgAJ" rel=3D"nofollow" onmousedown=3D"this.href=3D'javascript:';ret=
urn true;" onclick=3D"this.href=3D'javascript:';return true;">std-proposal.=
...@<wbr>isocpp.org</a>.<br>
To post to this group, send email to <a href=3D"javascript:" target=3D"_bla=
nk" gdf-obfuscated-mailto=3D"NhQZldkPAgAJ" rel=3D"nofollow" onmousedown=3D"=
this.href=3D'javascript:';return true;" onclick=3D"this.href=3D'javascript:=
';return true;">std-pr...@isocpp.org</a>.<br>
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/189d5497-36bb-4c9c-8992-13d7eaa36b6b%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter" target=3D"_blank" =
rel=3D"nofollow" onmousedown=3D"this.href=3D'https://groups.google.com/a/is=
ocpp.org/d/msgid/std-proposals/189d5497-36bb-4c9c-8992-13d7eaa36b6b%40isocp=
p.org?utm_medium\x3demail\x26utm_source\x3dfooter';return true;" onclick=3D=
"this.href=3D'https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/=
189d5497-36bb-4c9c-8992-13d7eaa36b6b%40isocpp.org?utm_medium\x3demail\x26ut=
m_source\x3dfooter';return true;">https://groups.google.com/a/<wbr>isocpp.o=
rg/d/msgid/std-<wbr>proposals/189d5497-36bb-4c9c-<wbr>8992-13d7eaa36b6b%40i=
socpp.org</a><wbr>.<br>
</div></blockquote></div></blockquote></div>
<p></p>
-- <br>
You received this message because you are subscribed to the Google Groups "=
ISO C++ Standard - Future Proposals" group.<br>
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br>
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br>
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/c0bef11f-1eba-48a2-b11d-8678207822ca%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.goo=
gle.com/a/isocpp.org/d/msgid/std-proposals/c0bef11f-1eba-48a2-b11d-86782078=
22ca%40isocpp.org</a>.<br>
</div></blockquote></body></html>
<p></p>
-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/752494EE-1737-4F41-B490-E951105527C2%=
40gmail.com?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/752494EE-1737-4F41-B490-E951105527C2%=
40gmail.com</a>.<br />
--Apple-Mail-65258858-2936-412D-99F5-0DCE3F2E0D90--
.