将空点与 T * 匹配

matching nullptr by T *

本文关键字:匹配      更新时间:2023-10-16

使用指向 AST 节点的指针boost::variant,这些节点可以包含特殊类型std::nullptr_t的值,表示空,我遇到了问题:表单[] (auto /* const */ * p) { /* use p */; }或表单的通用访问者:

struct V
{
    template< typename T >
    void operator () (T /* const */ * p)
    { /* use p */; }
};

无法处理std::nullptr_t类型的值。

人们可以想象有很多解决方法,但问题不断上升:是否有很好的解释为什么语言中没有(很可能受到高度限制的(decltype(*nullptr)类型(*nullptr格式不正确,std::remove_pointer_t< std::nullptr_t >libc ++std::nullptr_t(?这有理论上的原因吗?

有没有很好的解释为什么语言中没有(很可能是高度受限的(decltype(*nullptr(类型(*nullptr 格式不正确,libc ++ 中的 std::remove_pointer_t<std::nullptr_t> 是 std::nullptr_t(?这有理论上的原因吗?

我认为要回答这个问题,我们必须看看Herb Sutter和Bjarne Stroustrup提出的N1601。

有几个部分对我来说很突出,特别是

4.10 [转换]

空指针常量或类型 nullptr_t 的对象可以转换为 指针类型;结果是该类型的空指针值

和 4.11 [conv.mem]:

可以将空指针常量 (4.10( 或类型 nullptr_t (4.10( 的对象转换为指针 成员类型;结果是该类型的 NULL 成员指针值

因此,如果传递 nullptrnullptr_t 的结果是给定指针类型的空指针,那么取消引用它(例如,通过 decltype(*nullptr) 与取消引用任何其他类型的空指针相同是有意义的。(在delctype(*nullptr)的特定情况下,我相信这类似于取消引用空void*(。也就是说,你不应该这样做。

std::remove_pointer_t<std::nullptr_t> is std::nullptr_t

这是真的原因很容易,但为什么更难理解。

原因:

std::nullptr_t 是空指针文本 nullptr 的类型。它是一种独特的类型,它本身不是指针类型或指向成员类型的指针。

鉴于此,std::remove_pointer_t不会产生任何影响是有道理的nullptr_t因为它不是指针类型。

为什么

在N1601中,萨特和斯特劳斯特鲁普说

nullptr_t不是一个保留的词。它是在 中定义的 decltype(nullptr( 的 typedef(如其_t typedef 所示(。我们预计不会在实际程序中直接使用nullptr_t。

事实上,这似乎就是实际发生的事情。例如,Clang 3.9.0在stddef.h中具有以下内容:

namespace std { typedef decltype(nullptr) nullptr_t; }
using ::std::nullptr_t;

(而且他们关于在许多节目中出现的nullptr_t不多是正确的(。

这仍然不能解释为什么它被这样定义。要做到这一点,我认为我们需要回到更远一点的N1488,也是萨特和斯特劳斯特鲁普说的:

这种值的使用0表示C++中不同的东西(指针常量和int(导致了 至少自 1985 年以来,在教学、学习和使用C++方面存在问题。特别:

  • 区分空和零。无法区分空指针和整数0 很好地解决了过载问题。例如,给定两个重载函数f(int)f(char*), 呼叫f(0)明确地解决f(int).没有办法f(char*)用 空指针值,不写入显式强制转换(即 f((char*)0) (或使用命名变量。 请注意,这意味着今天的空指针 0 没有可言说的类型。

  • 命名空。此外,程序员经常要求空指针常量具有 名称(而不仅仅是0(。这是宏NULL存在的原因之一,尽管该宏是 不足。(如果空指针常量具有类型安全名称,这也将解决 以前的问题,因为它可以与过载分辨率的整数0区分开来,并且 一些错误检测。

我认为这很好地解释了原因;程序员需要一种方法来区分重载中的指针和整数值,并且由于NULL通常被定义为0,通常被解释为整数类型,因此没有简单的方法来强制重载分辨率选择指针重载。现在我们已经有了nullptr,我们可以区分指针和非指针类型,这完全避免了这个问题。