I recently switched from the Microsoft compiler to GCC. Among many things, I noticed that std::make_unique
became unavailable. This is apparently because make_unique
is not part of C++11 standard, and Microsoft just happened to include it as an extension.
We are planning to move to C++14 soon, but in the mean while I wrote this "shim" function, and put it into std
.
namespace std
{
template<typename T, typename... TArgs>
unique_ptr<T> make_unique(TArgs&&... args)
{
return std::unique_ptr<T>(new T(std::forward<TArgs>(args)...));
}
}
This solved that problem on gcc. However, I imagine it would cause problems back at Microsoft's side, since it would be a duplicate definition.
Is there a way to correctly shim functionality into std, so that it wouldn't cause trouble on different compilers / C++ standards? I looked around and didn't come up with anything. I thought maybe something like an include guard, for specific standard functions?
#ifndef MAKE_UNIQUE_DEFINED
#define MAKE_UNIQUE_DEFINED
// Define make_unique
#endif
Sadly it seems std
doesn't define include guards for specific functions. Is there anything else I can do to make this more correct and compiler / c++ standard agnostic?
You can, actually. Just turn the two conditions you mentioned in your post to a conditional macro definition:
#if defined(_MSC_VER) && __cplusplus == 201103L
# define MAKE_UNIQUE_DEFINED 1
#endif
_MSC_VER
checks the TU is compiled with MSVC. It's one of their predefined macros. You can use it to further refine the check, since it's the encoded version number of MSVC.
- 201103L is the value of
__cplusplus
when the TU is compiled as C++11 (this is standard, across platforms).
- In case
__cplusplus
isn't correctly defined for you (because Microsoft), you can use the _MSVC_LANG
macro in its stead.
The above can be used to wrap your "shim", as you originally planned. Or any other MSVC extension that pertains to these versions, as a matter of fact.
As an alternative, to avoid reopening the std
namespace (a no-no). You could use namespacing to have the definition present in a safe place, and control how your program interprets it:
namespace extended_std {
#ifdef MAKE_UNIQUE_DEFINED
inline namespace
#else
namespace
#endif
shim {
// your definition goes here
}
#ifndef MAKE_UNIQUE_DEFINED
using std::make_unique;
#endif
}
The macro is then only there to control what extended_std::make_unique
refers to. It either makes the namespace inline
, pouring its content into the enclosing one. Or adds a using declaration for std::make_unique
.
Simply put, do not define new functionality into std
. Define the function elsewhere and arrange to conditionally make it available per the other answer. Often for things like this, it is easier to get it from your own namespace on all platforms until all the compilers catch up. You will need to have some mechanism to manage the declarations and using statements for compatibility functions. (Another traditional approach is to use something like autoconfigure to sniff the compilation environment and custom build compatibility headers for your application. This is rather ugly but when done well it results in high levels of portability.)
For an industrial strength library which aims provide new C++ features in not quite up-to-date compilation environments, there is Abseil: https://github.com/abseil/abseil-cpp . The design decisions there are good to look at if you are interested in doing something robust for a significant project.
There is a predefined macro for this purpose: __cpp_lib_make_unique
See feature test macros
The following should be perfectly portable:
#include <memory>
#include <utility>
#ifdef __cpp_lib_make_unique
namespace lib {
using std::make_unique;
}
#else
namespace lib {
template<typename T, typename... TArgs>
std::unique_ptr<T> make_unique(TArgs&&... args)
{
return std::unique_ptr<T>(new T(std::forward<TArgs>(args)...));
}
}
#endif
You end up having make_unique
in another namespace, lib
in this example, but that is necessary for strict conformance
(see §17.4.3.1)
I say should because, unfortunately, MSVC does not define those
macros, so you have to use _MSC_VER
for MS specific things.