为什么 "bool c = nullptr ;" 能够编译通过(C++11)?

29
我不明白为什么以下代码编译通过?
int main()
{
     //int a = nullptr;  // Doesn't Compile
     //char b = nullptr; // Doesn't Compile
       bool c = nullptr; // Compiles

       return 0;
}

然而,注释部分不会。


我已经阅读了这个这个

布尔和空指针都是关键字,那么其他数据类型有什么独特之处呢?


4
这似乎很相关:http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#654 - Johannes Schaub - litb
1
"bool和nullptr都是关键字,那么其他数据类型有什么独特之处呢?" 如果您说“std :: nullptr_t p; int a = p; bool c = p;”,能否说明一下它们都是关键字的事实会有什么区别呢? - Johannes Schaub - litb
1
@JohannesSchaub-litb 抱歉让您感到困惑,我的疑问是为什么 nullptr 不能转换为基本数据类型,例如 charint - P0W
这个问题和答案适用于C++11,但对于语言规范的任何后续版本来说已经过时了。自从C++14以来,这种隐式转换不再允许出现在直接初始化环境之外。上述初始化代码无法编译通过。 - AnT stands with Russia
4个回答

42

出于同样的原因

if( p ) { ... }

编译:任何基本类型的值都会隐式转换为布尔值,其中0转换为false,其他任何值转换为true
最初,基本类型值必须转换为bool以实现C兼容性。C最初没有bool类型,但是任何数值表达式都可以用作布尔值(使用0 == false约定)。现在我们陷入了向后兼容的混乱中。nullptr必须支持惯用结构,例如if(p),特别是对于将旧代码的文字0NULL替换为nullptr的情况。例如,宏扩展或模板代码可能导致类似于if(p)的代码。
补充:为什么nullptr不会转换为int的技术细节。
由于nullptr隐式转换为bool,而bool(不幸地)隐式转换为int,因此可以期望nullptr也应该转换为int。但是nullptr的重点在于它应该像一个指针值一样行为。而指针虽然隐式转换为bool,但不会隐式转换为数值类型。
然而,为用户定义的类型安排这样的限制并不是完全简单的。如果存在operator bool转换,则将调用该转换以进行向int的转换。一个C++11解决方案是使转换运算符成为模板,并受到std::enable_if的限制,如下所示:
#include <type_traits>  // std::enable_if, std::is_same

struct S
{
    template< class Type >
    operator Type* () const { return 0; }

    template<
        class Bool_type,
        class Enabled = typename std::enable_if<
            std::is_same<Bool_type, bool>::value, void
            >::type
        >
    operator Bool_type () const { return false; }
};

auto main() -> int
{
    bool const              b   = S();      // OK.
    double const*  const    p   = S();      // OK.
    int const               i   = S();      // !Doesn't compile.
}

7
我不明白这个提供了什么理由。p不是一个空指针常量。你为什么要写字面上的 if(nullptr) { ... } - Johannes Schaub - litb
3
我认为有必要将指针转换为布尔值(甚至不考虑与C兼容性),整数也需要考虑C兼容性。但 nullptrnullptr_t 是C++中独有的,所以我不太明白这里的原因(这就是我撰写第一条评论的原因)。 - Johannes Schaub - litb
2
从技术上讲,nullptr_t 不需要转换为 bool。这可能有点奇怪,因为 if (nullptr) 会编译失败,而 if( ptr = nullptr ) 则会编译通过,但是... 然而,如果你编写了一个 template 函数,其中一个参数类似于指针,那么能够传递 nullptr 就很好了,然后 if 子句就会发生。 - Yakk - Adam Nevraumont
5
我希望 if( ptr = nullptr ) 会得到一个警告。 - Vladimir F Героям слава
5
在生成一个死分支(或平凡的“if”)的“if”中出现任务常常意味着使用了“=”而不是“==”。 - Yakk - Adam Nevraumont
显示剩余5条评论

19

C++11 §4.12布尔转换

算术、未作用域枚举、指针或成员指针类型的 prvalue 可以转换为类型为 bool 的 prvalue。零值、空指针值或空成员指针值转换为false;其他任何值转换为true。类型为 std::nullptr_t 的 prvalue 可以转换为类型为 bool 的 prvalue;转换后的值为false

虽然nullptr是关键字,但它是空指针文字(null pointer literal),不同于bool的角色。想一下布尔文字,truefalse也是关键字。


1
这个答案不涉及规则的理论基础,也就是问题提出者所要求的“为什么”。也就是说,它甚至并不是一个答案。但它也不是错误的。 - Cheers and hth. - Alf
2
@Cheersandhth.-Alf,我回答了两件事:<1>为什么nullptr可以转换为bool。<2>为什么它在语法上可以用于=的右侧,而显然你不能在那里放置bool(也是一个关键字)。因为nullptr是一个字面量,就像42true(另一个关键字是字面量)。也许我们对OP主要提出的问题有不同的看法,但我认为我的回答至少回答了OP的一些问题。 - Yu Hao
2
嗯,我认为<1>,“为什么”的问题还没有得到回答。但是<2>,关于类型与值的混淆,可能正是原帖作者所问的问题。我没有看到那一点,抱歉。 - Cheers and hth. - Alf
2
由于提问者没有指定他的编译器,因此他的“为什么'bool c = nullptr;'能编译”很可能不是旨在询问“为什么我的编译器接受这个?是为了满足客户?只是合规性吗?”(你的回答“因为标准要求<这里>”确实可以回答这个问题),但问题很可能是“为什么标准要在某个地方要求这样做?”,而你根本没有回答这个问题。我不明白“... 但它是一个空指针字面量,与bool的作用不同”想表达什么意思。如果您能澄清一下这一点,我会很高兴。 - Johannes Schaub - litb
@JohannesSchaub-litb我一开始没有回答这个问题,是因为我认为这不是主要问题,但在@Cheers和hth之后 - Alf更新了他的答案,我认为在这个问题上我无法提供比他更好的答案。我强调nullptrbool不是同一种关键字类型,它的角色是指针类型的字面量,就像bool类型的true一样,但正如我刚才看到的OP的评论,这不是他主要问的。如果我能想到与@Alf和您的答案不同的观点,我肯定会更新我的答案以更好地适应OP澄清的问题。 - Yu Hao

5
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#654中,Jason Merril提出了以下观点:

我们可以使用任意指针执行的操作,同样也应该能够使用nullptr_t执行。

我认为以下(稍微人为制造的)示例支持这个观点(尽管我不确定它是否是为这种情况而设计的)。

template<typename T, typename P>
void safeProcess(T pointer, P &processor) {
  bool isNonNull(pointer);
  if(isNonNull) {
    processor.process(pointer);
  }
}

这将允许传递nullptr以及与processor.process兼容的其他指针类型。


0

C++11通过引入一个新的关键字nullptr来纠正这个问题,作为一个特殊的空指针常量。它是nullptr_t类型的,可以隐式转换和比较任何指针类型或指向成员的指针类型。它不能隐式转换或与整数类型比较,除了bool类型。虽然最初的提案规定nullptr类型的右值不应该转换为bool类型,但核心语言工作组决定这样的转换是可取的,以保持与常规指针类型的一致性。这些提议的措辞变更在2008年6月的工作文件中被一致投票通过。

出于向后兼容性的原因,0仍然是一个有效的空指针常量。

char *pc = nullptr;     // OK
int  *pi = nullptr;     // OK
bool   b = nullptr;     // OK. b is false.
int    i = nullptr;     // error

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接