使用boost::variant
指向AST节点的指针,它可以包含特殊类型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
格式不正确 libc ++ 中的std::remove_pointer_t< std::nullptr_t >
是std::nullptr_t
?这有理论上的原因吗?
答案 0 :(得分:4)
有没有很好的解释为什么语言中没有(很可能是高度限制的)decltype(* nullptr)类型(* nullptr是格式错误的,而std :: remove_pointer_t&lt; std :: nullptr_t&gt;是std :: nullptr_t in的libc ++)?这有理论上的原因吗?
我想回答这个问题,我们必须看看Herb Sutter和Bjarne Stroustrup提出的N1601。
有几个部分对我很突出,特别是
4.10 [conv.ptr]
空指针常量或nullptr_t类型的对象可以转换为 指针类型;结果是该类型的空指针值
和4.11 [conv.mem]:
空指针常量(4.10)或nullptr_t(4.10)类型的对象可以转换为指针 成员类型;结果是该类型的空成员指针值
因此,如果传递nullptr
或nullptr_t
的结果是给定指针类型的空指针,那么取消引用它(例如,通过decltype(*nullptr)
将是有意义的与解除引用任何其他类型的空指针相同。(在delctype(*nullptr)
的特定情况下,我认为它类似于取消引用null void*
)。也就是说,你不应该这样做。
的std :: remove_pointer_t&LT; std :: nullptr_t&gt;是std :: nullptr_t
这是真的很简单,但为什么更难以获得。
std::nullptr_t是空指针文字的类型,nullptr。它是一种不同的类型,它本身不是指针类型或指向成员类型的指针。
鉴于此,std::remove_pointer_t
无效,因为nullptr_t
不是指针类型。
在N1601中,Sutter和Stroustrup说
事实上,这实际上似乎已经发生了。例如,Clang 3.9.0在stddef.h中有以下内容:nullptr_t不是保留字。它是&lt; cstddef&gt;中定义的decltype(nullptr)的typedef(如_t typedef所示)。我们不希望看到在实际程序中直接使用nullptr_t。
namespace std { typedef decltype(nullptr) nullptr_t; }
using ::std::nullptr_t;
(而且他们在许多程序中出现的nullptr_t
并不多。
这仍然没有解释 WHY 它是如此定义的。要做到这一点,我想我们需要回到N1488更远的地方,同时他们也会说:Sutter和Stroustrup:
使用值
0
表示C ++中的不同内容(指针常量和int
)导致 自1985年以来在教学,学习和使用C ++方面遇到的问题。特别是:
区分null和zero。无法区分空指针和整数
0
很好的超载分辨率。例如,给定两个重载函数f(int)
和f(char*)
, 来电f(0)
明确地解析为f(int)
。没有办法用{a}来拨打f(char*)
空指针值,无需编写显式强制转换(即f((char*)0)
)或使用命名变量。 请注意,这意味着今天的空指针0
没有可编辑的类型。命名为null。此外,程序员经常要求空指针常量具有 名称(而不仅仅是
0
)。这是宏NULL
存在的一个原因,尽管宏是 不足。 (如果空指针常量具有类型安全的名称,这也将解决 之前的问题,因为它可以与整数0
区分,用于重载解析和 一些错误检测。)
我认为这解释了为什么相当不错;程序员需要一种方法来区分重载中的指针和整数值,并且由于NULL
通常被定义为0
,通常被解释为整数类型,因此没有简单的方法来强制重载解析选择指针过载。现在我们有nullptr
,我们可以区分指针和非指针类型,它们完全避免了这个问题。