11

Is there a standard pointer class (or Boost) which is a non-shared pointer that works with incomplete types? I've gone over the C++11 standard and the boost library and can't find one, though it seems like a very useful type.

For example, I'd like to be able to make opaque types using a smart pointer.

  class A;
  wrap_ptr<A> some_func();
  void other_func( A const & );

A is an opaque type which can be used for a variety of functions. The user of the above interface has only an incomplete definition of A but should be able to delete/reset the pointer. I know the above can be done with a shared_ptr but that has an overhead I don't want in this particular code. unique_ptr has the right ownership semantics, but can't work with an incomplete type. In theory a wrapper should need only the overhead of having a pointer to a deleter.

Is there such a type in C++11 or the boost libraries?

NOTE: I understand I can easily build this type, but I'd prefer a standard type if possible. It seems like it should be a fundamental smart pointer type.


UPDATE: unique_ptr does not appear to be a good option. First off the syntax overhead would be offsetting. Secondly I'm not convinced it can be safely used with a custom deleter. I'll check to see how it might work.

edA-qa mort-ora-y
  • 30,295
  • 39
  • 137
  • 267

3 Answers3

10

To be clear about unique_ptr : it does work with incomplete type, but if you use it in a header of a class like that :

#include <memory>

class A;

class B
{

std::unique_ptr<A> m_a;

};

It will not link because of the missing deleter implementation. There is an easy fix to this : just define de destructor of the host class in a cpp, even if it's empty or should be the default one!

// B.hpp
#include <memory>

class A;

class B
{
public:

    B();
    ~B();

private:

std::unique_ptr<A> m_a;

};

// B.cpp

B::B(){} // or =default; (if you have a compiler providing it)
B::~B(){} // or =default; (if you have a compiler providing it)

Also, read answers to my question there : Is std::unique_ptr<T> required to know the full definition of T?

And maybe take a look at how pimpl idiom (implying uncomplete type in a unique_ptr) is recommended to be implmented by Herb Sutter: http://herbsutter.com/gotw/_100/

Community
  • 1
  • 1
Klaim
  • 67,274
  • 36
  • 133
  • 188
  • 1
    This isn't the use I want. I want to use the pointer in the return value of the function, in a place where the caller will never see the complete type. – edA-qa mort-ora-y Jan 30 '12 at 15:21
5

Actually, unique_ptr can work with incomplete types, as long as you specify a custom deleter.

However, contrary to shared_ptr, this actually influence its type, as the custom deleter is precised statically (as the second template parameter).

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • While technically true, using this directly is syntactically unfriendly. You can't simply do `ptr_type( new T )` but, assuming you get the template typedef correct, you must do `ptr_type( new T, T::~T )`. – edA-qa mort-ora-y Jan 30 '12 at 09:46
  • 1
    Actually, looking closely `unique_ptr` cannot use a custom deleter with a state. The `reset` function doesn't allow specifying a deleter, only the constructor does, therefore the custom deletion aspect of `unique_ptr` is partially broken. – edA-qa mort-ora-y Jan 30 '12 at 09:47
  • No custom deleter needed, the type only needs to be complete when the destructor of `unique_ptr` would be invoked, which means you can define the destructor after the held class is complete. – Xeo Jan 30 '12 at 13:20
  • 1
    @Xeo: First, The destructor is also necessary for move assignment and `reset` (I can't think of any other method). Secondly, the OP is not talking about a deferred definition, but about an Opaque Type. In C this is a typical case where the client never ever see the definition. – Matthieu M. Jan 30 '12 at 13:59
  • @edA-qamort-ora-y: "Actually, looking closely unique_ptr cannot use a custom deleter with a state." Not true. The standard specifically says that it will hold a copy of the deleter (20.7.1.2.1, p12). Just because `reset` can't reset the deleter's value doesn't mean the value is unused. `reset` doesn't take a deleter because deleters can be stored by reference in `unique_ptr`s. And thus you aren't guaranteed to be able to replace them. – Nicol Bolas Jan 30 '12 at 14:48
  • @NicolBolas: this might still be taken as an oversight. After all, it is impossible to use move assignment if the deleter type is a reference, and still it exists. So one could have provided two overloads of `reset`, with and without resetting the deleter. – Matthieu M. Jan 30 '12 at 15:04
  • @MatthieuM. An oversight? Perhaps; they could use the same wording they use with move assignment. But an oversight is a far cry from saying you can't have deleters with state *at all*. – Nicol Bolas Jan 30 '12 at 15:09
2

std::unique_ptr can handle this case, but not with the default deleter. You need to write a deleter that can handle an incomplete type, and then use std::unique_ptr<A,MyDeleter>.

Anthony Williams
  • 66,628
  • 14
  • 133
  • 155