C ++ std: .auto_ptr or std :: unique_ptr (to support multiple compilers, even older C ++ 03 compilers)?

I'm trying to update some C ++ code, I would like to move to more modern code (C ++ 11), but I still need to compile the code with some older compilers (C ++ 03 compatible), due to the supported platform limitations.

I know that std :: auto_ptr is deprecated in C ++ 11 compilers, but due to older compiler support, I cannot just replace them with std :: unique_ptr.

Is there a good practice to handle this "old compiler support, but start moving towards C ++ 11"?

+2


source to share


3 answers


As you noted, std :: auto_ptr <> is deprecated in C ++ 11 ( Reference ).

Moving to C ++ 11 std :: unique_ptr <> is the correct way to go, as also pointed out by Herb Sutter in GotW89 :

  1. What is an auto_ptr trade?
    auto_ptr is most vividly described as a valiant attempt to create a unique_ptr before C ++ transfers semantics. auto_ptr is now deprecated and should not be used in new code.
    If you have auto_ptr in your existing codebase when you get the chance to try a global lookup and replace auto_ptr with unique_ptr; the vast majority of uses will work the same, and it might be detected (like a compile-time error) or fix (quietly) an error or two that you didn't know you had.

Also note that C ++ 17 will remove std :: auto_ptr.

I think there might be different ways to solve your problem, the "correct" one also depends on how your actual code is written.
Several variants:

Option 1

Use boost :: unique_ptr

Option 2

Conditional use auto_ptr or unique_ptr based on __cplusplus.

class Myclass {
#if __cplusplus <201103L
std :: auto_ptr m_ptr;
#else
std :: unique_ptr m_ptr;
#endif
...

This will be scattered all over the places where you refer to auto_ptr, I don't like that.
It might seem less awkward if all your references to std :: auto_ptr have already been printed (just conditionally change the typedef).



Option 3

Conditional use usage and aliases to "define" auto_ptr (and refer to it without std :: namespace).

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

Disadvantage: you keep using "auto_ptr", but in C ++ 11 this means std :: unique_ptr.
In fact they are confused ...

Option 3.1

Probably slightly better than option 2: the
other way around, using aliases and preferring the name unique_ptr.

Option 4

Wrap a std :: smart pointer (conditionally auto_ptr or unique_ptr) in your own smart pointer class.
This can be cumbersome and requires finding and replacing all auto_ptr references with your new class.

Other dirty options

Other options include definitions inside std :: namespace, which I think is prohibited by the standard,
or using the #define preprocessor for ... ehm ... "rename" unique_ptr to auto_ptr only for older C ++ 03 compilers.

+4


source


In this particular case, you can start modernizing your code with help boost::unique_ptr

instead auto_ptr

, which is not recommended.



In general, most of the C ++ 11 libraries are straight out of boost, so it's a good place to start.

+3


source


If I was serious I would create a namespace notstd

.

The goal would be that in C ++ 11 it notstd

consists of a series of type aliases std

. In C ++ 03, it has pseudo C ++ 11 code.

C ++ 03 code notstd

will not be 100% compatible with C ++ 11 std

, but rather valid C ++ 03 notstd

will in turn lead to valid C ++ 11.

For example, I could use special tagged references like reference-wrapper for references notstd::move

instead of rvalue references, and my move-only types require such a tagged "reference wrapper".

namespace notstd {
  template<class U>
  struct moved_t {
    U& u;
    // relies on NRVO, which is pretty universally supported.
    // also relies in U being default-constructible:
    operator U()const{ U tmp; std::swap(u, tmp); return tmp; }
  };
  template<class U>
  moved_t<U> move(U& u) { return {u}; }
  template<class T>
  struct unique_ptr {
    unique_ptr( moved_t<unique_ptr<T>> > ); // move ctor
    template<class U>
    unique_ptr( moved_t<unique_ptr<U>> > ); // move ctor
  private:
    unique_ptr(unique_ptr const&);
  };
}

      

This way your code will use notstd::unique_ptr<T>

. There would be no implicit rvalue conversion, so every place you move notstd::unique_ptr<T>

you need notstd::move

one.

Unfortunately, this means that it notstd::unique_ptr<T>

cannot be placed in containers std

. It might need to do some hacks to get them working (not safe auto_ptr

).

This is not a trivial task. boost::unique_ptr

was an attempt to generate unique unique_ptr semantics in C ++ 03 and they will do a better job than you most likely. If you can't use them directly, read what they did and override it yourself.

+1


source







All Articles