16

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?

3
  • 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, 2015 at 8:59
  • As an aside, you may want to fold Foo::Initialize into Foo::Foo.
    – MSalters
    Jul 29, 2015 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).
    – user118658
    Jul 29, 2015 at 10:05

3 Answers 3

18

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 in your own "private" namespace
  • Once you 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 as all the current auto_ptr references need to be changed in the code to something like my_ptr<T>::ptr
  • Better safety because 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
9
  • 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).
    – user118658
    Jul 29, 2015 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, 2015 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, 2015 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=...).
    – user118658
    Jul 29, 2015 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.
    – user118658
    Jul 29, 2015 at 11:30
0

Option 5: Direct alias.

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

Tradeoffs:

  1. For newer language versions, AKA C++11 and later, your alias type maps to the correct smart pointer. Any user code that actually depends on APIs specific to std::auto_ptr will get flagged by the compiler, which is the ultimate guarantee that it'll really be fixed.

  2. In Legacy c++03 mode the type alias is a macro. This is gross, but the resulting syntax MyPtr<T> will be identical to the C++11 case throughout the rest of the code.

  3. You have to find and change all your auto_ptr variables to MyPtrin order to set this up.

2
  • 1
    It's very unclear what this is referencing (and, as phrased, it isn't a question at all).
    – autophage
    Jan 16, 2019 at 19:53
  • 1
    @autophage I believe its an answer... so probably not a question.
    – Kain0_0
    Jan 17, 2019 at 0:35
0

“deprecated” means you can still use it if you have a good reason. Having to support an older compiler version is a good reason. I would work until a decision is made that development for cxx03 is stopped and no new development will work with 03. And you have that in writing by your management.

At that point you rip out the old code and replace it with new code. And if someone complains you show them the written statement.