Re: Cast from shared_ptr<X>* to shared_ptr<const X>*
Am 23.09.2011 21:03, schrieb Thiago Adams:
Hi,
I understand that the types shared_ptr<X> and shared_ptr<const X>
are independent types.
It is of little importance, whether both types are independent or not
for the question that you are asking here (at least if I understand your
question correctly). It seems to me that you want to reinterpret a
reference or a pointer to shared_ptr<X> as shared_ptr<const X>. This
requires a property called "layout-compatible", see 9.2 p19+20:
"19 If a standard-layout union contains two or more standard-layout
structs that share a common initial sequence, and if the standard-layout
union object currently contains one of these standard-layout structs, it
is permitted to inspect the common initial part of any of them. Two
standard-layout structs share a common initial sequence if corresponding
members have layout-compatible types and either neither member is a
bit-field or both are bit-fields with the same width for a sequence of
one or more initial members.
20 A pointer to a standard-layout struct object, suitably converted
using a reinterpret_cast, points to its initial member (or if that
member is a bit-field, then to the unit in which it resides) and vice
versa. [..] "
based on the definition of a layout compatible struct in p17:
"Two standard-layout struct (Clause 9) types are layout-compatible if
they have the same number of non-static data members and corresponding
non-static data members (in declaration order) have layout-compatible
types (3.9)."
However, in practice, I think the cast between these two (at this
specific case) are safe.
Or am I wrong?
The standard does not give this guarantee, because it doesn't say that
shared_ptr<T> and shared_ptr<const T> are layout compatible. The
standard does not even promise that any instance of shared_ptr is an
object of a standard-layout class type and I would argue that it would
be very astonishing if it were (The typical implementations I know of
have base classes or data members with virtual functions).
It is quite probable, that this works for many implementations, but
strictly speaking it is undefined behaviour.
I did a performance test comparing argument passing using shared_ptrs
in cases where the cast or copies are necessary.
For instance, cast from shared_ptr<Derived> do shared_ptr<Base> or
shared_ptr<X> to shared_ptr<const X>.
void f(const shared<const X>& s) {}
shared<X> sp;
f(sp);
In this sample the performance is penalized because of the reference
counting
necessary to convert types.
(Complete source code: http://www.thradams.com/codeblog/smartptrperf.htm)
I also understand that the cast from shared_ptr<Derived>* to
shared_ptr<Base>*
can be dangerous, but I don?t see any problem in the case
shared_ptr<X>* to
shared_ptr<const X>*.
If this is true, why not increase the performance creating a cast
operator
at the shared_ptr ?
I'm unsure whether implementations are willing to provide this
guarantee. Let me present an artificial example just to point out what
*could* went wrong:
template<class T>
struct shared_base
{
virtual ~shared_base() {}
};
template<class T>
struct shared_ptr : shared_base<T>
{
T* p;
operator const shared_ptr<const T>& () const
{
return *((shared_ptr<const T>*)(this));
}
operator shared_ptr<const T>& ()
{
return *((shared_ptr<const T>*)(this));
}
};
#include <typeinfo>
#include <iostream>
void foo(const shared_ptr<int>& p) {
std::cout << typeid(p).name() << std::endl;
}
void bar(const shared_ptr<const int>& p) {
std::cout << typeid(p).name() << std::endl;
}
int main() {
shared_ptr<int> p1;
foo(p1);
bar(p1);
shared_ptr<const int> p2;
bar(p2);
}
This program outputs for me on gcc 4.7:
10shared_ptrIiE
10shared_ptrIiE
10shared_ptrIKiE
This means I can detect by runtime that the "reinterpreted"
shared_ptr<const int> from bar(p1) has a *different* typeid than
the real one from bar(p2), in other words: it has essentially the typeid
of the original shared_ptr<int>.
Let me emphasize that this is just a demonstration example, not one
based on an existing implementation. I only want to point out a
potential risk for real implementations to get this right.
HTH & Greetings from Bremen,
Daniel Kr?gler
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]