如何正确地将功能填充到std中

How to correctly shim functionality into std?

本文关键字:std 填充 功能 正确地      更新时间:2023-10-16

我最近从Microsoft编译器切换到GCC。在许多事情中,我注意到std::make_unique变得不可用。这显然是因为make_unique不是C++11标准的一部分,而微软只是碰巧将其作为扩展。

我们计划很快转到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++标准不可知?

实际上你可以。只需将您在文章中提到的两个条件转换为条件宏定义:

#if defined(_MSC_VER) && __cplusplus == 201103L
#  define MAKE_UNIQUE_DEFINED 1
#endif
  • _MSC_VER检查TU是否使用MSVC编译。这是他们预定义的宏之一。您可以使用它来进一步细化检查,因为它是MSVC的编码版本号
  • 201103L是当TU编译为C++11时__cplusplus的值(这是跨平台的标准)
  • 如果__cplusplus没有为您正确定义(因为Microsoft),您可以使用_MSVC_LANG宏来代替它

按照您最初的计划,以上内容可用于包裹您的"垫片"。或者与这些版本相关的任何其他MSVC扩展。


作为一种替代方案,以避免重新打开std命名空间(a no no)。你可以使用名称空间将定义放在一个安全的地方,并控制你的程序如何解释它:

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中定义新功能。在其他地方定义函数,并安排根据其他答案有条件地使其可用。通常,对于这样的事情,在所有平台上从自己的命名空间中获取它会更容易,直到所有编译器都赶上为止。您需要有一些机制来管理声明并使用兼容性函数的语句。(另一种传统的方法是使用自动配置之类的东西来探查编译环境和应用程序的自定义构建兼容性标头。这很难看,但如果做得好,会导致高级别的可移植性。)

对于一个旨在在不太先进的编译环境中提供新的C++功能的工业级库,有Abseil:https://github.com/abseil/abseil-cpp。如果你有兴趣为一个重要的项目做一些稳健的事情,那么那里的设计决策是很好的。

有一个预定义的宏用于此目的:__cpp_lib_make_unique
请参阅功能测试宏

以下应该是完全可移植的:

#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

您最终在另一个名称空间lib中拥有make_unique在本例中,但这对于严格一致性是必要的(见§17.4.3.1)

我说应该,因为不幸的是,MSVC并没有定义宏,所以您必须使用_MSC_VER来处理特定于MS的事情。