std::unique_ptr to reference of std::unique_ptr of base class

4,633 views
Skip to first unread message

zongshe...@gmail.com

unread,
May 18, 2018, 3:39:02 AM5/18/18
to ISO C++ Standard - Discussion
As we know, reference of std::unique_ptr cannot be binded to std::unique_ptr of derived class. Code shown bellow cannot compile

std::unique_ptr<Derived> ptr(new Derived);
std::unique_ptr<Base>& ref = ptr; // Compilation error here

However, this could be useful in some cases, like calling function with passing by reference.
I understand that this is disabled because unique_ptr<Derived> cannot implicitly converted to unique_ptr<Base> because copy constructor is deleted. But actually we could achieve this by the following member function template:

template<typename Target, typename Deleter>
operator my_unique_ptr<Target, Deleter>&()
{
static_assert(std::is_convertible_v<my_unique_ptr::pointer, typename my_unique_ptr<Target, Deleter>::pointer>, "cannot convert");
return reinterpret_cast<my_unique_ptr<Target, Deleter>&>(*this);
}

Above code just does some very basic checking, but at least shows that it is possible to override conversion operator to bind reference of std::unique_ptr to std::unique_ptr of derived class. 

Therefore, I wonder why reference of std::unique_ptr cannot be binded to std::unique_ptr of derived class? Is there any special reason for disabling this behavior? Or there are some technical difficulties that I have not realized?

Thiago Macieira

unread,
May 18, 2018, 11:45:33 AM5/18/18
to std-dis...@isocpp.org
On Friday, 18 May 2018 00:39:02 PDT zongshe...@gmail.com wrote:
> I understand that this is disabled because unique_ptr<Derived> cannot
> implicitly converted to unique_ptr<Base> because copy constructor is
> deleted. But actually we could achieve this by the following member
> function template:
>
> template<typename Target, typename Deleter>
> operator my_unique_ptr<Target, Deleter>&()
> {
> static_assert(std::is_convertible_v<my_unique_ptr::pointer, typename
> my_unique_ptr<Target, Deleter>::pointer>, "cannot convert");
> return reinterpret_cast<my_unique_ptr<Target, Deleter>&>(*this);
> }

This only works if the internal storage of std::unique_ptr<Derived> and
std::unique_ptr<Base> are identical. That is not the case.

Your code breaks if Base and Derived have different top-of-structure addresses
in almost every single std::unique_ptr implementation. Try this:

struct Base { int i; };
struct OtherBase { int j; };
struct Derived : OtherBase, Base {};

Create a unique_ptr<Derived> and do your cast trick to convert it to
unique_ptr<Base> Then perform a comparison of the two .get(). You'll see the
comparison will fail.

In fact, you don't need a unique_ptr to do this test. Just try this:

auto dptr = new Derived;
auto base = reinterpret_cast<Base *&>(dptr);
assert(base == dptr);

This assertion will fail.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center



ZONGSHENG ZHAO

unread,
May 23, 2018, 12:20:28 AM5/23/18
to std-dis...@isocpp.org
Yes, that's correct. But it's fixable. 

template<typename Target, typename Deleter>
operator my_unique_ptr<Target, Deleter>&()
{
	static_assert(std::is_convertible_v<my_unique_ptr::pointer, typename my_unique_ptr<Target, Deleter>::pointer>, "cannot convert");
	auto& temp = reinterpret_cast<my_unique_ptr<Target, Deleter>&>(*this);
	temp.ptr = this->ptr;
}
I know that this implementation is naive and ignored many special cases, but I believe there should not be any case that is really difficult to deal with. 




--

---
You received this message because you are subscribed to a topic in the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this topic, visit https://groups.google.com/a/isocpp.org/d/topic/std-discussion/3-Yf14jZrzQ/unsubscribe.
To unsubscribe from this group and all its topics, send an email to std-discussion+unsubscribe@isocpp.org.
To post to this group, send email to std-dis...@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.

T. C.

unread,
May 23, 2018, 5:18:08 AM5/23/18
to ISO C++ Standard - Discussion, zongshe...@gmail.com
There's a reason why not even built-in pointers - hardly a pinnacle of safety - support such insanity.

Derived1* d = new Derived1;
Base*& b = d; // nope
b = new Derived2; // ouch, d is now not pointing to a Derived1 anymore.

You have yet to produce a scenario in which such a use is "correct" even in an extremely loose sense that ignores the flock of nasal demons clawing at you for tearing the object model to shreds.

Thiago Macieira

unread,
May 23, 2018, 9:13:03 AM5/23/18
to std-dis...@isocpp.org
On Wednesday, 23 May 2018 01:20:25 -03 ZONGSHENG ZHAO wrote:
> Yes, that's correct. But it's fixable.
>
> template<typename Target, typename Deleter>operator
> my_unique_ptr<Target, Deleter>&(){
> static_assert(std::is_convertible_v<my_unique_ptr::pointer, typename
> my_unique_ptr<Target, Deleter>::pointer>, "cannot convert");
> auto& temp = reinterpret_cast<my_unique_ptr<Target,
> Deleter>&>(*this); temp.ptr = this->ptr;}
>
> I know that this implementation is naive and ignored many special cases,
> but I believe there should not be any case that is really difficult to deal
> with.

It doesn't just ignore special cases, it ignores fundamentals. Please fix the
fundamentals before you propose the feature. You also forgot to return
something, but I'll assume that's just a mistake of coding in the email client
instead of actually compiling and testing.

But that shows you're not putting enough effort into this. Please *try* first.
Your example above modified the "this" object so that the pointer it has is
now adjusted to the target class. Did you really mean to make the source
object no longer usable?

Not to mention that it will very likely crash when the deleter runs.
Reply all
Reply to author
Forward
0 new messages