We are testing a library under C++11 (i.e., -std=c++11). The library uses auto_ptr and this pattern:

Foo* GetFoo()
{
    autoptr<Foo> ptr(new Foo);

    // Initialize Foo
    ptr->Initialize(...);

    // Now configure remaining attributes
    ptr->SomeSetting(...);

    return ptr.release();
}

C++11 deprecated auto_ptr, so we want to move away from it.

However, the code supports both C++03 and C++11, so its not a simple as yanking auto_ptr. It's also worth mentioning the library has no external dependencies. It uses C++03; and does not use Autotools, Cmake, Boost, ...

How should we handle the design changes to move away from auto_ptr for C++11 while retaining compatibility with C++03?

  • Are any of the auto_ptr scoped (i.e. std::auto_ptr), do they need to be or can the smart pointer be obtained from some other namespace? – Niall Jul 29 '15 at 8:59
  • As an aside, you may want to fold Foo::Initialize into Foo::Foo. – MSalters Jul 29 '15 at 9:42
  • 1
    @MSalters - yeah, that has always been one of those things I have felt mildly uncomfortable about. The library was designed in the 1990s, and I think the design was similar to MFC. That is, there was lower level C++ construction, and then a "higher level" object construction. I think the feature was used as a tradeoff so classes don't have 6 or 12 different constructors. (At this point, what I have done is gone through and ensured the member variables of POD types are initialized to sane defaults in the C++ constructors). – jww Jul 29 '15 at 10:05
up vote 9 down vote accepted

In most respects the std::unique_ptr was made to be drop in (but safer) replacement for std::auto_ptr, so there should be very few (if any) code changes required other than (as you ask) directing the code to use either unique_ptr or auto_ptr.

There a few ways to do this (and each comes with its own list tradeoffs) below. Given the code sample provided, I would favour either of the first two options.

Option 1

#if __cplusplus >= 201103L
template <typename T>
using auto_ptr = std::unique_ptr<T>;
#else
using std::auto_ptr;
#endif

Tradeoffs;

  • You introduce the auto_ptr name into the global namespace; you can mitigate this by defining it is your own "private" namespace
  • Once migrate to C++17 (I believe auto_ptr will be completely removed) you can more easily search and replace

Option 2

template <typename T>
struct my_ptr {
    #if __cplusplus >= 201103L
    typedef std::unique_ptr<T> ptr;
    #else
    typedef std::auto_ptr<T> ptr;
    #endif
};

Tradeoffs;

  • Probably more cumbersome to work with, all the current auto_ptr need to changed in the code to something like my_ptr<T>::ptr
  • Better safety the names are not being introduced into the global namespace

Option 3

Somewhat controversial, but if you are prepared to put up with the caveats of having a std class as a base

#if __cplusplus >= 201103L
template <typename T>
using my_ptr = std::unique_ptr<T>;
#else
template <typename T>
class my_ptr : public std::auto_ptr<T> {
  // implement the constructors for easier use
  // in particular
  explicit my_ptr( X* p = 0 ) : std::auto_ptr(p) {}
};
#endif

Tradeoffs;

  • Don't try to use the inherited class where a virtual base (in particular w.r.t. the non-virtual destructor) would be expected. Not that this should be an issue in the case - but be aware of it
  • Again, code changes
  • Potential namespace mismatches - it all depends on how the pointer class is used to begin with

Option 4

Wrap the pointers in a new class and aggregate the required functions to the member

template <typename T>
class my_ptr { // could even use auto_ptr name?
  #if __cplusplus >= 201103L
  std::unique_ptr<T> ptr_;
  #else
  std::auto_ptr<T> ptr_;
  #endif

  // implement functions required...
  T* release() { return ptr_.release(); }
};

Tradeoffs;

  • A little extreme when all you really want is to "swap" the implementations out
  • Very good answer. I actually researched it a bit, and you hit at least three of the tests I tried. (What you lack is the OS X and Clang specific stuff. OS X is a bear because it still uses TR1 namespace for C++03 at times, and you have to include things using this method: No type named 'unique_ptr' in namespace 'std' when compiling under LLVM/Clang). – jww Jul 29 '15 at 10:07
  • @jww. I'm on OS X (XCode 6.4 and Apple LLVM version 6.1.0 (clang-602.0.53) (based on LLVM 3.6.0svn)) and have no issues with the C++03/11 mix other than the tr1 namespace no longer being there (I do use libc++ and not libstdc++). I know tr1 was non-normative, but I can't find anywhere in the draft (here) that the files had to be <tr1/...> at all, infact it mentions just being in the header <memory> etc. file just in the tr1 namespace. – Niall Jul 29 '15 at 11:17
  • @jww. I guess that given a particular mix of compiler, library and target device - you may need to do a few more hand stands. Else, on OS X, consider moving to clang and libc++. Frankly I consider the libc++ to be the new "native" C++ library to OS X - I would default to that. I have no way to support these claims other that the history of the clang/Apple relationship and that the GCC tools on OS X seem out of date (library) or just removed (as far as I know GCC is a thin stub to clang anyway). – Niall Jul 29 '15 at 11:24
  • "Else, on OS X, consider moving to clang and libc++..." - yeah, I kind of agree with you. However, we would like to let the users make that choice, and not force it upon them. (They implicitly make the choice when they specify (or lack) CXX=...). – jww Jul 29 '15 at 11:27
  • Here's the case that is causing me so much trouble on OS X 10.7 and 10.8: c++ -v -std=c++11 -x c++ - < /dev/null. I grep'd the include directories that were dumped, and they do not include unique_ptr. – jww Jul 29 '15 at 11:30

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.