为std :: string分配true / false:发生了什么?

时间:2012-03-12 16:23:23

标签: c++ string gcc g++ std

我在我的源代码上测试了一个c ++ 11编译器,它在我的一个函数中发现了一个错误,我希望我的非c ++ 11编译器能够捕获它。我从一个返回类型为std :: string的函数返回false ...这是演示问题的代码

#include <iostream>

int main ( )
{
    std::string str = false;

    std::cerr << "'" << str << "'" << std::endl;

    return 0;
}


$ g++ test.cpp -W -Wall -Wextra
$ ./a.out

terminate called after throwing an instance of 'std::logic_error'
  what():  basic_string::_S_construct NULL not valid
Aborted

我很惊讶这段代码编译没有任何问题。我怀疑异常描述是编译器将false转换为0然后转换为NULL并将其用作char *来尝试构造字符串..

然而,当我将false切换为true时,这就是我得到的:

$ g++ test.cpp -W -Wall -Wextra
test.cpp: In function ‘int main()’:
test.cpp:5: error: conversion from ‘bool’ to non-scalar type ‘std::string’ requested

在我看来,这是一个更合理的结果。

有人可以澄清为什么这种看似不一致的行为会发生吗?也就是说,std::string a = false编译,但抛出异常,std::string a = true不编译。

编辑:

作为参考,这是用g ++ 4.7生成的错误,其中-std = c ++ 11用于错误情况:

test.cpp: In function ‘int main()’:
test.cpp:5:23: 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]

尽管CashCow建议

,它确实接受NULL

4 个答案:

答案 0 :(得分:6)

这是一种可怕的隐性转换,缺乏类型安全性。

std::string从指针获取构造函数 false降级为0,成为空指针。

并且您不能将空指针传递给std :: string的构造函数。

顺便提一下,当你使用=时,它是一个构造函数而不是你在这里执行的任务。

您的“严格”g ++ C ++ 11编译器在编译时很好地捕获了错误。

它不适用于true,因为它永远不能表示NULL指针。 C ++ 11有nullptr。如果你尝试过:

std::string str = nullptr;

您的C ++ 11编译器可能会编译它,然后您会收到运行时错误。

答案 1 :(得分:3)

正如你所说,false可以转换为有效的空指针常量(遗憾的是)。

但是,

true不是空指针常量,不能转换为1,因此无法转换为指针而无法编译。

§4.5 Integral promotions [conv.prom] p4

  

bool类型的prvalue可以转换为int类型的prvalue, false变为零true变为一。

§4.10 Pointer conversions [conv.ptr] p1

  

空指针常量是整数类型的整数常量表达式(5.19)prvalue,其评估为零或类型为std::nullptr_t的prvalue。

由于false是一个文字,它也是一个整数常量表达式,在推广之后确实评估为零。

请注意,这在C ++ 11中没有改变。 实际上,上述引用来自C ++ 11标准。你用GCC 4.7获得的只是一个警告。这是一个可选的诊断程序,编译器决定提示,因为它总是错误的并且是一个错误。

答案 2 :(得分:2)

这是一个我可能无法完全理解的微妙问题。

基本规则是任何值为0的内容都可以被视为有效的空指针。因此,false可以在需要指针的上下文中使用,例如char const*

但是,std::string中的char const*构造函数明确需要一个非空指针(在这里,您很幸运能获得异常)。

另一方面,true不是0,因此不能被视为指针。因此,您可以获得适当的诊断。


Richard Smith提出的C ++ 11中constexpr的引入加剧了这个问题:

struct S { constexpr S(): n() {} int n; };

这里,S().n被评估为0静态(constexpr要求),因此可能退化为指针,而在C ++ 03中它是int类型。这是相当不幸的,如果你有:

std::true_type buggy(void*);
std::false_type buggy(int);

然后decltype(buggy(S().n))为C ++ 11返回true_type,但在C ++ 03中返回false_type,这是一个相当不幸的语义变化。

理查德的建议是将隐式转换更改为标准转换,以便在这种情况下提供帮助,但我认为这对您的帮助不大。

Clang警告可用于那些奇怪的转换:-Wbool-conversions

答案 3 :(得分:0)

Xeo's answer是正确的,包括C ++ 11。

在C ++ 14中,§4.10 Pointer conversions [conv.ptr] p1中的相关文本已更改:

  

空指针常量整数文字(2.14.2),其值为零或prvalue类型为std::nullptr_t

(我大胆强调)

所以false通过§4.5 Integral promotions [conv.prom] p4保持整数常量表达式,它不是整数字面§2.14.2 Integer literals [lex.icon] p1

  

整数文字是一个没有句号或指数部分的数字序列,可选择分隔单引号,在确定其值时会被忽略。

gcc 4.5对此发出警告,无论-std=c++1?标志如何,gcc 6.1都将此视为错误。

Visual C++ 19.10.25109.0 from Visual Studio 2017愉快地编译它,因此不符合C ++ 14。我找不到一个开放的Microsoft Visual Studio开发人员社区问题,所以我lodged one

此问题于2012年以CWG1448的形式提交给ISO C ++标准委员会,并作为CWG903的解决方案的一部分修订(2009年提出,但在2013年得到解决)。

标准措辞的更改在CWG903 Value-dependent integral null pointer constants处可见,它将以下文本块添加到从C ++ 03到当前标准的差异列表中: §C2.2 Clause 4: standard conversions [diff.cpp03.conv]

  

更改:只有文字是整数空指针常量

     

基本原理:删除与模板和常量表达式的令人惊讶的交互

     

对原始功能的影响:有效的C ++ 2003代码可能无法在本国际标准中编译或产生不同的结果,如下例所示:

     

void f(void *); // #1 void f(...); // #2 template<int N> void g() { f(0*N); // calls #2; used to call #1 }

有趣的是,这种转换在CWG97中被认为是奇怪的,但不是问题的关键,所以显然没有做任何事情。

  

[...]我们有一个异常的概念,即truefalse不是常量表达式。

     

现在,您可能会争辩说不应该允许您将false转换为指针。但是[...]