如何正确地将功能填充到标准中?
How to correctly shim functionality into std?
我最近从 Microsoft 编译器切换到 GCC。在许多事情中,我注意到 std::make_unique
变得不可用。这显然是因为 make_unique
不是 C++11 标准的一部分,Microsoft 恰好将其作为扩展包含在内。
我们计划很快转向 C++14,但与此同时,我编写了这个 "shim" 函数,并将其放入 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)...));
}
}
这解决了 gcc 上的问题。但是,我想这会在微软方面引起问题,因为它是一个重复的定义。
有没有办法将功能正确填充到 std 中,从而不会在不同的编译器/C++ 标准上造成问题?我环顾四周,没有想到任何东西。我想也许像 include guard 这样的东西,用于特定的标准功能?
#ifndef MAKE_UNIQUE_DEFINED
#define MAKE_UNIQUE_DEFINED
// Define make_unique
#endif
遗憾的是,std
似乎没有为特定函数定义包含保护。我还能做些什么来使它更正确并且与编译器/c++ 标准无关?
实际上你可以。只需将您在 post 中提到的两个条件转换为条件宏定义即可:
#if defined(_MSC_VER) && __cplusplus == 201103L
# define MAKE_UNIQUE_DEFINED 1
#endif
_MSC_VER
检查 TU 是用 MSVC 编译的。 It's one of their predefined macros。您可以使用它来进一步细化检查,因为它是 MSVC 的编码版本号。
- 201103L is the value of
__cplusplus
当 TU 被编译为 C++11(这是标准的,跨平台的)。
- 如果
__cplusplus
没有为您正确定义(因为 Microsoft),您可以使用 _MSVC_LANG
宏代替它。
以上内容可用于包装您的 "shim",正如您最初计划的那样。或者事实上,与这些版本相关的任何其他 MSVC 扩展。
作为替代方案,避免重新打开 std
命名空间(禁忌)。您可以使用命名空间将定义放在安全的地方,并控制您的程序如何解释它:
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
}
宏只是用来控制 extended_std::make_unique
指的是什么。它要么创建命名空间 inline
,将其内容注入封闭的命名空间。或者为 std::make_unique
.
添加 using 声明
简单地说,不要在 std
中定义新功能。在其他地方定义功能,并安排有条件地使其根据其他答案可用。通常对于这样的事情,在所有的编译器都赶上之前,从所有平台上你自己的命名空间中获取它会更容易。您将需要某种机制来管理兼容性函数的声明和使用语句。 (另一种传统方法是使用自动配置之类的东西来嗅探编译环境和自定义构建兼容性 headers 为您的应用程序。这相当丑陋,但如果做得好,它会带来高水平的可移植性。)
对于旨在在不完全 up-to-date 编译环境中提供新的 C++ 功能的工业强度库,有 Abseil:https://github.com/abseil/abseil-cpp。如果您有兴趣为重要项目做一些稳健的事情,那里的设计决策很值得一看。
有一个用于此目的的预定义宏:__cpp_lib_make_unique
参见 feature test macros
以下应该 是完美便携的:
#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
你最终在另一个命名空间中有 make_unique
,lib
在此示例中,但这是严格遵守所必需的
(参见 §17.4.3.1)
我说 应该 因为不幸的是,MSVC 没有定义那些
宏,所以你必须使用 _MSC_VER
来处理特定于 MS 的事情。
我最近从 Microsoft 编译器切换到 GCC。在许多事情中,我注意到 std::make_unique
变得不可用。这显然是因为 make_unique
不是 C++11 标准的一部分,Microsoft 恰好将其作为扩展包含在内。
我们计划很快转向 C++14,但与此同时,我编写了这个 "shim" 函数,并将其放入 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)...));
}
}
这解决了 gcc 上的问题。但是,我想这会在微软方面引起问题,因为它是一个重复的定义。
有没有办法将功能正确填充到 std 中,从而不会在不同的编译器/C++ 标准上造成问题?我环顾四周,没有想到任何东西。我想也许像 include guard 这样的东西,用于特定的标准功能?
#ifndef MAKE_UNIQUE_DEFINED
#define MAKE_UNIQUE_DEFINED
// Define make_unique
#endif
遗憾的是,std
似乎没有为特定函数定义包含保护。我还能做些什么来使它更正确并且与编译器/c++ 标准无关?
实际上你可以。只需将您在 post 中提到的两个条件转换为条件宏定义即可:
#if defined(_MSC_VER) && __cplusplus == 201103L
# define MAKE_UNIQUE_DEFINED 1
#endif
_MSC_VER
检查 TU 是用 MSVC 编译的。 It's one of their predefined macros。您可以使用它来进一步细化检查,因为它是 MSVC 的编码版本号。- 201103L is the value of
__cplusplus
当 TU 被编译为 C++11(这是标准的,跨平台的)。 - 如果
__cplusplus
没有为您正确定义(因为 Microsoft),您可以使用_MSVC_LANG
宏代替它。
以上内容可用于包装您的 "shim",正如您最初计划的那样。或者事实上,与这些版本相关的任何其他 MSVC 扩展。
作为替代方案,避免重新打开 std
命名空间(禁忌)。您可以使用命名空间将定义放在安全的地方,并控制您的程序如何解释它:
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
}
宏只是用来控制 extended_std::make_unique
指的是什么。它要么创建命名空间 inline
,将其内容注入封闭的命名空间。或者为 std::make_unique
.
简单地说,不要在 std
中定义新功能。在其他地方定义功能,并安排有条件地使其根据其他答案可用。通常对于这样的事情,在所有的编译器都赶上之前,从所有平台上你自己的命名空间中获取它会更容易。您将需要某种机制来管理兼容性函数的声明和使用语句。 (另一种传统方法是使用自动配置之类的东西来嗅探编译环境和自定义构建兼容性 headers 为您的应用程序。这相当丑陋,但如果做得好,它会带来高水平的可移植性。)
对于旨在在不完全 up-to-date 编译环境中提供新的 C++ 功能的工业强度库,有 Abseil:https://github.com/abseil/abseil-cpp。如果您有兴趣为重要项目做一些稳健的事情,那里的设计决策很值得一看。
有一个用于此目的的预定义宏:__cpp_lib_make_unique
参见 feature test macros
以下应该 是完美便携的:
#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
你最终在另一个命名空间中有 make_unique
,lib
在此示例中,但这是严格遵守所必需的
(参见 §17.4.3.1)
我说 应该 因为不幸的是,MSVC 没有定义那些
宏,所以你必须使用 _MSC_VER
来处理特定于 MS 的事情。