modern-cpp-tutorial icon indicating copy to clipboard operation
modern-cpp-tutorial copied to clipboard

可否像维基百科一样引入争议性内容出处。

Open mobs001 opened this issue 1 year ago • 6 comments

今天刚开始看这本书。第 2 章 语言可用性的强化,2.1 里写 nullptr ,描述为NULL具有很奇怪的简单的逻辑设计失误, 这个理由很反直觉,按理说 处理关键词 单纯是编译器的设计,如果不是为了兼容性,NULL在编译器层面很好处理, 我简单查了一下,网上更多的说法是 C++ 从前是为了保持了 C的兼容性 保持了 NULL的一致性,既然保持兼容性那NULL必然是保持一致的弱类型。 然后 后来权衡下引入了安全的类型nullptr, NULL作为遗留性继续兼容C。

感觉这就是单纯的决心增加类型安全性nullptr, 而不是在这点上保持与旧的C一致性的的选择问题。 和逻辑完全无关。 然后我又查了下后来的C23也开始增加了nullptr类型。不过并未进一步查询是否一定的原因是为了增加两种语言的兼容性。

文中举例的代码,在msvc下以cxx_std_20 编译 是可以通过的

#include <iostream>
#include <type_traits>

void foo(char *);
void foo(int);

int main() {
    if (std::is_same<decltype(NULL), decltype(0)>::value)
        std::cout << "NULL == 0" << std::endl;
    if (std::is_same<decltype(NULL), decltype((void*)0)>::value)
        std::cout << "NULL == (void *)0" << std::endl;
    if (std::is_same<decltype(NULL), std::nullptr_t>::value)
        std::cout << "NULL == nullptr" << std::endl;

    foo(0);          // 调用 foo(int)
   foo(NULL);    // !在msvc下面能通过编译
    foo(nullptr);    // 调用 foo(char*)
    return 0;
}

void foo(char *) {
    std::cout << "foo(char*) is called" << std::endl;
}
void foo(int i) {
    std::cout << "foo(int) is called" << std::endl;
}

mobs001 avatar Dec 19 '24 14:12 mobs001

感觉书里关于nullptr 的来历动机, 和网上的内容形成了很大争议,如果方便,请标注一下权威的出处。

mobs001 avatar Dec 19 '24 14:12 mobs001

2.1 里写 nullptr ,描述为NULL具有很奇怪的简单的逻辑设计失误

这个理解和 WG21 N2431 没什么冲突。

NULLdecltype(NULL) 类型的其他值在类型系统中的行为不一致,我觉得这就属于逻辑设计失误了。

frederick-vs-ja avatar Jan 02 '25 14:01 frederick-vs-ja

原来理解来自于这里,应该标注上。 不过我对提交这个讨论的人同样不苟同,弱类型应该不能通过思维逻辑到强类型上。 这个单纯是增不增加类型的问题。 保持弱类型,它肯定就是弱类型,总不能搞运行时的动态语言吧。

个人非常难以接受c++现代化之前很多跳大神的行为。 感觉那段时间的标准委员会几乎都是原教主义的激进份子。 最严重的是 长期试图使用模板跳大神模式达成编译器的行为,这直接逼走了一大批用户(并且还洋洋自得,看我模板图灵完备)。 许多比c++还不工业化的语言,都选择了直接开放编译器中间件的模式, 比如JavaScript+babel, 这样语法直观又简洁。 dirty work 和用户的项目完全隔离开, 不干扰程序真正试图表达的语义。

不过目前来看c++ 17 以后一切都在变好, 虽然其他没有接触过 新c++的用户,还保留着过去c++恶劣不堪的印象。

mobs001 avatar Jan 03 '25 06:01 mobs001

我估计下面示例代码不能复现的原因,应该较为类似 #289 提出的原因

mobs001 avatar Jan 03 '25 06:01 mobs001

这句话说的没有太大问题, 具体可见HOPL4 Bjarne Stroustrup自己写的描述. 在那篇论文里简单自然地描述了nullptr是如何设计出来的

There is nothing fundamentally wrong with this statement; for further detail, see Bjarne Stroustrup’s own explanation in HOPL4, where he describes in a simple and natural way how nullptr was designed.

HOPL4

4.2.6 nullptr

在 C 和 C+‍+ 中,如果将字面量 0 赋值给指针或与指针比较时它表示空指针。更令人困惑的是,如果将任何求值为零的整数常量表达式赋值给指针或与指针比较时它也表示空指针。例如:

int* p = 99-55-44; // 空指针
int* q = 2;        // 错误:2 是一个 int,而不是一个指针

这使很多人感到烦恼和困惑,因此有一个标准库宏 NULL(从 C 中采用),它在标准 C+‍+ 中定义为 0。某些编译器会对 int* p = 0 提出警告;但是我们仍然没法为函数针对指针和整数重载而避免 0 的歧义。

这很容易通过给空指针命名来解决,但是不知何故没有人能提出一份人们能达成一致的提议。在 2003 年的某个时候,我正通过电话参加一个会议,讨论如何给空指针命名。如 NULLnullnilnullptr0p 等建议名都是备选方案。照旧,那些简短而“漂亮”的名字已经被使用了成千上万次,因此不能在不破坏数百万行代码的情况下使用。我听了数十次这样的讨论,有点厌烦了,只是在似听非听。人们说到 null pointer、null ptr、nullputter 的变体。我醒过来说:“你们都在说 nullptr。我想我没有在代码中看到过它。”

Herb Sutter 和我写下了该提案 [Sutter and Stroustrup 2003],该提案在 2007 年相对容易地通过了(仅仅进行了四次小修订后),所以现在我们可以说:

int* p0 = nullptr;
int* p1 = 99-55-44;  // 可以,为了兼容性
int* p2 = NULL;      // 可以,为了兼容性

int f(char*);
int f(int);

int x1 = f(nullptr); // f(char*)
int x2 = f(0);       // f(int)

我对 nullptr 的发音是“null pointer”。

我仍然认为如能将宏 NULL 定义为 nullptr 可以消除一类重要的问题,但委员会认为这一改变过于激进。

MosZhang7 avatar Nov 24 '25 03:11 MosZhang7

回到原先的问题。身为作者写下一段没有多言的判断,很可能是因为作者相信没有争议。我想很难让人对于一个自己相信没有争议的结论产生这种意识。

frederick-vs-ja avatar Nov 24 '25 06:11 frederick-vs-ja