【问题标题】:implementation safe nullptr实现安全的 nullptr
【发布时间】:2025-11-27 20:10:02
【问题描述】:

我想让我的代码在旧版 C++(使用“NULL”的 C++ 代码)和新的 C++11 标准(使用“nullptr”的 C++ 代码)上都可编译

我正在使用 GCC,但计划在我完成最重要的事情时也为 VS 重新编译整个代码库。

我是否应该期望 GCC 和 VS 都会做类似的事情

#define NULL nullptr

或者我自己做会更好(当然使用不同的名称,其中 MY_LIB 将替换为我的库后缀)?

#ifndef nullptr
    #define MY_LIB_NULL NULL
#else
    #define MY_LIB_NULL nullptr
#endif

我想要实现的是无论是否实现了 C++11 功能都可以编译的代码(而且由于我没有使用模板,所以模板很少)。

例如关键字“override”和“final”已经完成。

MY_LIB_OVERRIDE //macro, defines to "override" if c++11 is present.
MY_LIB_FINAL    //macro, defines to "final" if c++11 is present.

我问这个问题是因为我知道“nullptr”问题有点奇怪,所以也许只是对覆盖和最终做同样的事情是错误的。需要对此发表意见。欢迎任何帮助。

【问题讨论】:

  • 好像有些编译器实现者已经将NULL定义为“nullptr”,所以最安全的方法是直接使用NULL,如果nullptr存在就使用,如果nullptr不存在,那么NULL就是仍然安全.. 如果我们已经有 NULL,为什么还要使用“nullptr”?还是有可能实现者会放弃“NULL”(我认为极不可能)
  • #define NULL nullptr 目前不应该合法,而且可能永远不会合法。 NULL 必须扩展为一个值为 0 的整数常量表达式。
  • 哇,我很惊讶这样的改变是允许的;这有可能破坏大量代码(该代码可以说是“坏”代码,但委员会无论如何都不喜欢破坏代码)。不过,我的立场是正确的。不,我不认为 NULL 会因为同样的原因而被删除:破坏代码,不管你喜不喜欢,通常都不能这样做。
  • @DarioOO 不能具体检测到nullptr,但是可以检测C++11:#if __cplusplus >= 201103
  • 我没有在这个系统上安装 GCC 4.7,但它的值取决于你是否使用 -std=c++11/-std=gnu++11 进行编译。在线浏览GCC source code,这已经实现(尽管不一定适用于 4.7)。

标签: c++ c++11 overriding final nullptr


【解决方案1】:

您可以通过以下方式创建 my_nullptr_t 类型的“假”my_nullptr

const class my_nullptr_t
{
    public:

        /* Return 0 for any class pointer */
        template<typename T>
        operator T*() const
        {
            return 0;
        }

        /* Return 0 for any member pointer */
        template<typename T, typename U>
        operator T U::*() const
        {
            return 0;
        }

        /* Safe boolean conversion */
        operator void*() const
        {
            return 0;
        }

    private:

        /* Not allowed to get the address */
        void operator&() const;

} my_nullptr = {};

这适用于 C++03 和 C++11,并且应该始终是安全的,无论实现了哪个 C++11 功能。该解决方案实际上已经在this topic 中讨论过,它提出了基于the Official proposalnullptr_t 版本。

【讨论】:

  • 我认为它在技术上应该有operator==operator!=operator bool(),尽管代码不会经常使用它们。
  • explicit operator bool() 会很棒,但除非您已经使用 C++11,否则无法使用。 t 似乎不需要operator==operator!=:与任何指针进行比较都可以。
  • 对,你只需要重载operator==operator!=,因为双方都是my_nullptr_t。这很愚蠢,但一些模板实例化可能会做到这一点。
  • 我仔细查看了关于 nullptr 提案的论文 (n2431.pdf),并且已经查看了 * 上的相关主题,所以这个答案是重复的。实现“my_nullptr_t”的缺点是比只使用 NULL (本文第 3 页结尾)更糟糕,所以我将在 C++11 不可用时使用 NULL,并且仅在 #if __cplusplus >= 201103 时使用“nullptr”(关键字)(就像已经由hvd建议)。我想将他的评论标记为最佳答案。但他发表了评论,而不是答案。
  • 那么对于重复的答案很抱歉。发布此答案后,我发现了上述主题。顺便说一句,为了完整性,我只是添加了operator void*() const 的重载以进行安全的布尔转换。
【解决方案2】:

NULL 是一个扩展为空指针常量的宏。它仍然像以前一样工作。必须使用非 C++11 编译器的代码应使用 NULL

【讨论】:

    【解决方案3】:

    我认为以下方法可行:

    #include <cstddef>
    
    #ifndef MY_LIB_NULL
        #ifndef NULL //check for NULL
            #define MY_LIB_NULL nullptr
        #else
            #define MY_LIB_NULL NULL ///use NULL if present
        #endif
    #endif
    

    基本上我检查“NULL”。 wich 是一个宏并且可以检查,直到编译器附带该宏(可能是),而不是使用宏有效,当编译器将仅提供“nullptr”并且不再具有 NULL 时使用 nullptr(可能在一个遥远的未来,但似乎我们可以愉快地继续使用 NULL!)

    我认为这比重新定义“nullptr”更安全(就像大多数人试图做的那样)

    【讨论】:

    • &lt;cstddef&gt; 是定义宏 NULL 所必需的,因此对 NULL 的测试只会在极其不合格的实现上失败。
    • 不是在每个具有 cstddef 标头的实现中都定义了 NULL 吗?
    • 是的,NULL 需要在&lt;cstddef&gt; 中定义,并且&lt;cstddef&gt; 需要存在于每个符合要求的实现中。
    • 所以它将被定义为符合要求的实现。不符合要求的实现将无法编译,因为未定义 NULL 以及诸如 ptr = NULL; 之类的东西。变成 ptr = nullptr;(如果旧的 c++ 代码存在 nullptr 不是编译器知道的标记)这是想要的行为(至少在我的情况下,如果某些东西不符合至少旧的 C++ 代码我不期望符合 C++11)
    • 是的,尽管我怀疑任何地方都没有定义 NULL 的实现,尽管最安全的选择是 stddef.h。这是古老的 C 语言。