我在我的源代码上测试了一个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答案 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中被认为是奇怪的,但不是问题的关键,所以显然没有做任何事情。
[...]我们有一个异常的概念,即
true
和false
不是常量表达式。现在,您可能会争辩说不应该允许您将
false
转换为指针。但是[...]