通过 T * 匹配 nullptr

matching nullptr by T *

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

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

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

有很多可以想象的解决方法,但问题出现了:是否有很好的解释为什么没有(很可能受到高度限制)decltype(*nullptr) 类型的语言(*nullptr 是病态的-formed 并且 std::remove_pointer_t< std::nullptr_t >std::nullptr_t in libc++)?这有理论上的原因吗?

is there good explanation why there no (very likely highly restricted) decltype(*nullptr) type in the language (*nullptr is ill-formed and std::remove_pointer_t< std::nullptr_t > is std::nullptr_t in libc++)? Are there theoretical reasons for this?

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

有几个部分让我印象深刻,尤其是

4.10 [conv.ptr]

A null pointer constant or an object of type nullptr_t can be converted to a pointer type; the result is the null pointer value of that type

和 4.11 [conv.mem]:

A null pointer constant (4.10) or an object of type nullptr_t (4.10) can be converted to a pointer to member type; the result is the null member pointer value of that type

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

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

这是真的原因很简单,但原因却很难理解。

原因:

std::nullptr_t is the type of the null pointer literal, nullptr. It is a distinct type that is not itself a pointer type or a pointer to member type.

鉴于此,std::remove_pointer_t 没有效果是有道理的,因为 nullptr_t 不是指针类型。

原因

在 N1601 中,Sutter 和 Stroustrup 说

nullptr_t is not a reserved word. It is a typedef (as its _t typedef indicates) for decltype(nullptr) defined in <cstddef>. We do not expect to see much direct use of nullptr_t in real programs.

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

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

(而且他们说的不多 nullptr_t 出现在很多节目中也是对的)。

这仍然不能解释为什么它是这样定义的。要做到这一点,我认为我们需要回到更远一点的时间 N1488,同样是 Sutter 和 Stroustrup,他们说:

This use of the value 0 to mean different things (a pointer constant and an int) in C++ has caused problems since at least 1985 in teaching, learning, and using C++. In particular:

  • Distinguishing between null and zero. The null pointer and an integer 0 cannot be distinguished well for overload resolution. For example, given two overloaded functions f(int) and f(char*), the call f(0) unambiguously resolves to f(int). There is no way to write a call to f(char*) with a null pointer value without writing an explicit cast (i.e., f((char*)0)) or using a named variable. Note that this implies that today’s null pointer, 0, has no utterable type.

  • Naming null. Further, programmers have often requested that the null pointer constant have a name (rather than just 0). This is one reason why the macro NULL exists, although that macro is insufficient. (If the null pointer constant had a type-safe name, this would also solve the previous problem as it could be distinguished from the integer 0 for overload resolution and some error detection.)

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