不同 gcc 版本的 bool 到 string 转换的不同行为

Different behavior for bool to string conversion with different gcc versions

我知道我不应该这样做,但我仍然想知道它是如何工作的。

我正在尝试编译一个错误代码,其中 return 类型的函数是 std::string 而我实际上是 return 一个布尔值:

// test.cpp
std::string donothing(int i)
{
  return false;
}

我知道这不应该起作用,错误应该被编译器捕获,但有一个有趣的观察结果:

  1. 使用 gcc 4.8.5,我只是摆脱了警告和编译工作。
  2. 使用 gcc 7.3.1,编译失败。

编译器输出如下:

// gcc 4.8.5
test.cpp: In member function ‘virtual std::string donothing(int)’:
test.cpp:9:9: warning: converting ‘false’ to pointer type for argument 1 of ‘std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’ [-Wconversion-null]
      return false;

// gcc 7.3.1
test.cpp: In member function ‘virtual std::__cxx11::donothing(int)’:
test.cpp:9:9: error: could not convert ‘false’ from ‘bool’ to ‘std::__cxx11::string {aka std::__cxx11::basic_string<char>}’
      return false;
             ^~~~~

海湾合作委员会 7.3.1

默认情况下,GCC 7.3.1 使用 -std=gnu++14。 (我找不到 GCC 7.3.1 手册,但 FWIW,GCC 7.4 manual and the GCC 6.5 manual 都这么说。)

让我们看一下C++14([basic.string])中classbasic_string的概要。唯一接收一个参数的转换 (non-explicit) 构造函数是:1

basic_string(const charT* s, const Allocator& a = Allocator());

因此,问题的本质是:false不能隐式转换为const char*。每 [conv.ptr]/1:

A null pointer constant is an integer literal ([lex.icon]) with value zero or a prvalue of type std::nullptr_t. A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type and is distinguishable from every other value of object pointer or function pointer type. Such a conversion is called a null pointer conversion. Two null pointer values of the same type shall compare equal. The conversion of a null pointer constant to a pointer to cv-qualified type is a single conversion, and not the sequence of a pointer conversion followed by a qualification conversion ([conv.qual]). A null pointer constant of integral type can be converted to a prvalue of type std::nullptr_t. [ Note: The resulting prvalue is not a null pointer value. — end note ]

false 是布尔文字 ([lex.bool]) 而不是整数文字。因此,false不能隐式转换为const char*.

1 其实有一个 initializer_list 构造函数符合条件,但是因为明显无关所以省略了

海湾合作委员会 4.8.5

根据 GCC 4.8.5 manual:

The default, if no C++ language dialect options are given, is -std=gnu++98.

我们再来看看C++98([lib.basic.string])中classbasic_string的概要。唯一接收一个参数的转换 (non-explicit) 构造函数是:

basic_string(const charT* s, const Allocator& a = Allocator());

因此,问题的本质是:false可以隐式转换为const char*。每 [conv.ptr]/1:

A null pointer constant is an integral constant expression (expr.const) rvalue of integer type that evaluates to zero. A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type and is distinguishable from every other value of pointer to object or pointer to function type. Two null pointer values of the same type shall compare equal. The conversion of a null pointer constant to a pointer to cv-qualified type is a single conversion, and not the sequence of a pointer conversion followed by a qualification conversion (conv.qual).

false 是整数类型的整数常量表达式右值(bool 是整数类型),其计算结果为零。因此,false可以隐式转换为const char*.