2

I need to populate container with shared pointers and raw pointers at same time.

I guess shared_ptr<T> may be forced to behave like T*, if constructed with no-op deleter and no-op (de)allocator?

Or may be there is universal smart pointer, which binds to different (smart and raw) pointer types?

Or may be shared_ptr can point to one object, but manage lifetime of another (nullptr) object of same type?

Background. I have simple component system. Some components are built-in, no management required, raw pointers will be enough. Other components are external DLLs, they must be attached when requested and detached when removed from "session". For latter I am using wrapper component, that will detach DLL when destroyed.

EDIT: Background updated.
EDIT2: Question updated.
EDIT3: Found straight solution. See my answer if interested.

Shadows In Rain
  • 1,140
  • 12
  • 28
  • 1
    You can get the raw pointer out of a shared_ptr. Use `.get()`. Just watch ownership – Tony The Lion Apr 26 '13 at 15:02
  • I don't get why you would want to have a no-op deleter in either case. *Different* deleters, yes. But why manage your components' lifetime manually? – Arne Mertz Apr 26 '13 at 15:04
  • 2
    @ArneMertz: Who says it's manual? It could be static or automatic; if its lifetime is wider than the container's, or it removes itself from the container on destruction, then there's no problem storing a non-owning pointer in the container. – Mike Seymour Apr 26 '13 at 15:09
  • 1
    "*Background. I have simple component system. Some components are built-in, some must be attached and then detached (DLLs).*" That doesn't explain why this container needs to *own* those pointers. Why can't it just be a container of pointers to unowned objects, objects who's lifetime is managed elsewhere? And more to the point, if you need to own those pointers, why would you not need to own *some* of them? – Nicol Bolas Apr 26 '13 at 15:16
  • This just sounds like simple OO design. Create a "Component" base class and two subclasses BuiltinComponent and ExternalComponent with the appropriate virtual functions to do whatever you do to them. One will be implmented with the raw pointer and one with a shared pointer. Store pointers to the base class in your container... – jcoder Apr 26 '13 at 15:16
  • @jcoder: That would assume that the `T` in question isn't already a base class with derived classes as an already existing hierarchy. Also, it doesn't really solve the ownership issue. – Nicol Bolas Apr 26 '13 at 15:17
  • @jcoder I'm already using similiar approah. I have `universal_ptr`, that wraps (`operator->`) around raw pointer, but may hold also `shared_ptr`. But I suspect that same may be achieved with just `shared_ptr`. – Shadows In Rain Apr 26 '13 at 15:47
  • @TonyTheLion Component system is only owner of components. – Shadows In Rain Apr 26 '13 at 15:48
  • @NicolBolas Because I wanted to make lifetime management optional, keeping interface as simple as possible. – Shadows In Rain Apr 26 '13 at 16:11

3 Answers3

2

Generally speaking, no; a container contains exactly one type of object.

You could use some kind of boost::variant<shared_ptr<T>, T*> object as the contained object. But you'd need visitors to access the elements of it. Alternatively, you can give boost::shared_ptr a special deleter object that doesn't actually delete the pointed-to value.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
2

Well, it really depends on what your needs are, and more specifically what you would like to achieve in terms of ownerships:

  1. Should your vector own the shared_ptr<T> ? (or could you just store the pointee ?)
  2. Should your vector own the T* ?

Then there are varied solutions.

  1. Full ownership: just encapsulate the T* in a regular shared_ptr<T>
  2. Partial ownership: as stated, you can just instantiate a shared_ptr<T> with a no-op deleter to wrap the T*
  3. No ownership: just unwrap the shared_ptr<T> using .get() and only store T*
Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
1

Shared pointer may point to one object, but manage lifetime of another object. Curiously, it may manage nothing, but still point to something. There is special constructor, that accepts another shared pointer, that used to determine which object to share. We can pass empty shared pointer in it.

template <typename T>
std::shared_ptr<T> fake_shared(T *ptr)
{
    std::shared_ptr<T> dummy (nullptr); // we have nothing to manage;
    std::shared_ptr<T> faked (dummy, ptr); // but we have something to point to;
    return faked;
}

Moreover, we can exploit 2 facts. Firstly, this constructor will accept shared pointer of any (other) type. Secondly, std::shared_ptr<void> is legal, and may be used to express our intent more clearly.

template <typename T>
std::shared_ptr<T> fake_shared(T *ptr)
{
    std::shared_ptr<void> dummy (nullptr); // we have really nothing to manage;
    std::shared_ptr<T> faked (dummy, ptr); // but we have something to point to;
    return faked;
}

Alternatively, one may use constructor, that accepts custom deleter, and pass no-op deleter into it.

template <typename T>
std::shared_ptr<T> fake_shared(T *ptr)
{
    std::shared_ptr<T> faked (ptr, [](T *){}); // won't be deleted;
    return faked;
}

EDIT: Revision, shared void, custom deleter.

Shadows In Rain
  • 1,140
  • 12
  • 28
  • For those interested: [8th constructor](http://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr). Amusing, I had missed this. I believe it's originally intended to allow to have a pointer within a `struct` whilst ensuring the whole `struct` remains alive. Definitely interesting. *And completely non-obvious here*, I would still encourage you to reach for a no-op deleter: it's more explicit. – Matthieu M. Apr 27 '13 at 11:12
  • @Matthieu I don't like approach with deleter. I feel it's more intrusive, thus more error-prone. It's like saying "don't delete this object". Why? Need to add comment, so it's not obvious again. Instead, I will prefer to say "we have nothing to manage, just point here". It's closer to root cause. Deleters are better for deleting something. – Shadows In Rain Apr 28 '13 at 01:14
  • @Matthieu Also I have incapsulated this into function, so there will be no difference for end user, anyway. – Shadows In Rain Apr 28 '13 at 01:17
  • 1
    Whether to prefer one or the other is certainly subjective... but for what it's worth I think you are doing the right thing encapsulating it in a method, this way you can switch at leisure. – Matthieu M. Apr 28 '13 at 10:49