【问题标题】:How to correctly shim functionality into std?如何正确地将功能填充到标准中?
【发布时间】:2017-10-24 08:31:45
【问题描述】:

我最近从 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++ 标准上造成麻烦?我环顾四周,没有想出任何东西。我想可能是针对特定标准功能的包含守卫?

#ifndef MAKE_UNIQUE_DEFINED
#define MAKE_UNIQUE_DEFINED
// Define make_unique
#endif

可悲的是,std 似乎没有为特定功能定义包含防护。我还能做些什么来使它更正确并且与编译器/c++ 标准无关?

【问题讨论】:

  • 从技术上讲,§17.4.3.1 使得除了模板特化之外,无法将功能可移植地插入到 std 命名空间中
  • 在“理论”中,我希望根据标准这是不允许的,请参阅stackoverflow.com/a/41062439/417197en.cppreference.com/w/cpp/language/extending_std。当然,在实践中,它会工作得很好。
  • 您使用的 MSVC 版本可能已经默认支持 C++14 功能。如果您使用的是新的 MSVC 版本,您可以明确指定您想要的 C++ 版本(从 14 开始)。

标签: c++ std


【解决方案1】:

事实上,你可以。只需将您在帖子中提到的两个条件转换为条件宏定义:

#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 宏来代替它。

如您最初计划的那样,以上内容可用于包裹您的“垫片”。或者事实上与这些版本相关的任何其他 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 声明。

【讨论】:

  • 只有关键字inline 有条件编译有什么意义?
  • 为此目的还有预定义的__cpp_lib_make_unique
  • 抱歉,我没有想到 inline 命名空间。我认为你最好明确一点,inline namespacenamespace
  • @Cheersandhth.-Alf - 我只有条件地编译 inline 关键字,以控制被内联的命名空间。当然有多种选择。可以完全避免命名空间并有条件地编译整个定义。
【解决方案2】:

简单地说,不要在std 中定义新功能。在别处定义该功能,并安排有条件地根据其他答案使其可用。通常对于这样的事情,在所有平台上从您自己的命名空间中获取它会更容易,直到所有编译器都赶上。您将需要一些机制来管理声明和使用声明以实现兼容性功能。 (另一种传统方法是使用诸如自动配置之类的东西来嗅探编译环境并为您的应用程序自定义构建兼容性标头。这相当难看,但如果做得好,它会带来高水平的可移植性。)

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

【讨论】:

    【解决方案3】:

    为此目的有一个预定义的宏:__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
    

    您最终在另一个命名空间lib 中拥有make_unique 在此示例中,但这是严格遵守所必需的 (见§17.4.3.1

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

    【讨论】:

    • 你的意思是微软开始定义这些宏?那一定是最近的。
    • "MSVS" (Microsoft Visual Studio) 应该是 "MSVC" (Microsoft Visual C/C++ Compiler)。
    猜你喜欢
    • 1970-01-01
    • 2016-04-19
    • 2013-02-16
    • 1970-01-01
    • 1970-01-01
    • 2021-12-28
    • 2021-05-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多