Lig*_*ica 2 c++ gcc visual-studio language-lawyer
使用以下测试用例:
#include <iostream>
void foo()
{}
int main()
{
   std::cout << &foo << std::endl;
}
GCC 4.1.2,GCC 4.8和GCC 4.9(C++ 03和C++ 11)在构建和编译时都给出以下输出:
$ g++ main.cpp -o test && ./test
main.cpp: In function 'int main()':
main.cpp:8:23: warning: the address of 'void foo()' will always evaluate as 'true' [-Waddress]
   std::cout << &foo << std::endl;
                 ^
1
这应该是因为函数指针唯一可行的流插入是转换为bool(并且void*需要强制转换才能实际获取流中的地址).
但是,Microsoft Visual Studio 2012和2013会输出指针地址.
哪一套工具链符合要求?是否记录了不合格?
Pra*_*ian 10
bool如果禁用语言扩展(/Za切换),可以使MSVC正常运行并执行从函数指针的转换.如果这样做,您的代码会产生以下警告(在/W4VS2013上)
1>main.cpp(8): warning C4305: 'argument' : truncation from 'void (*)(void)' to 'std::_Bool'
1>main.cpp(8): warning C4800: 'void (*)(void)' : forcing value to bool 'true' or 'false' (performance warning)
而输出是 1
这种行为被记录下的类型转换部分
C++编译器和C编译器都支持这些非ANSI转换:
...
非ANSI转换为指向数据指针的函数指针
果然,以下行仅编译为/Za禁用
void *p = &foo;
禁用语言扩展会产生错误消息
1>main.cpp(8): error C2440: 'initializing' : cannot convert from 'void (*)(void)' to 'void *'
1>          There is no context in which this conversion is possible
至少通过我阅读N3337,gcc是正确的,MSVC是不正确的(除非你禁用它的扩展名).
路径从标准的§4开始:
标准转化是具有内置含义的隐式转化.第4条列举了全套此类转换.
因此,唯一存在的标准转换是第4节中列出的转换.但并非每种可能的标准转换都适用于所有情况.只能使用符合标准转换序列的那些.标准转换序列规定如下:
- 来自以下集合的零或一次转换:左值到右值的转换,数组到指针的转换以及函数到指针的转换.
- 来自以下集合的零或一次转换:整数促销,浮点促销,积分转换,浮点转换,浮点积分转换,指针转换,成员转换指针和布尔转换.
- 零或一个资格转换.
这里我们从指向函数的指针开始,因此第一个项目符号点下的转换不适用.我们不需要/关心资格转换,所以我们也不关心第三个要点.
转换pointer to function为pointer to void显然是指针转换.这些正好有三个品种.在§4.10/ 1,我们有从空指针常量开始的指针转换(这里显然不适用).§4.10/ 2涵盖从以下开始的转换:
类型为"指向cv T的指针"的prvalue,其中T是对象类型[...]
这显然也不适用于此,因为函数不是对象.第三种选择是:
类型为"指向cv D的指针"的prvalue ,其中D是类类型[...]
同样,函数不是类类型,因此也不能应用.
这使我们只有一个选项:直接从"指向函数的指针"到"布尔"的单个转换.当然,这是一个布尔转换.§4.12说:
算术,无范围枚举,指针或指向成员类型的指针的prvalue可以转换为bool类型的prvalue.
因此,当且仅当1)它是prvalue时,我们的值才能转换为布尔值,并且2)它是指针.这似乎很明显,但如果我们想确认,我们可以在§5.3.1/ 2和5.3.1/3中查看运算符地址的定义:
以下每个一元运算符的结果都是prvalue.
这满足了第一个要求.
一元&运算符的结果是指向其操作数的指针.操作数应为左值或限定ID.如果操作数是一个qualified-id,命名一个类型为T的某个类C的非静态成员m,则结果类型为"指向类型为C的C类成员的指针",并且是一个指定C :: m的prvalue.否则,如果表达式的类型是T,则结果具有"指向T的指针",并且是作为指定对象(1.7)的地址的prvalue或指向指定函数的指针.[强调补充]
这显然符合第二个要求 - 结果是一个指针.
由于满足了这些要求,转换可能/将会发生.转换结果如下(返回§4.12):
零值,空指针值或空成员指针值转换为false; 任何其他值都转换为true.
因为我们从指向实际函数的指针开始,所以我们不能有空指针.这只留下一种可能性:"任何其他值都转换为真."
正如gcc所说的那样,转换的唯一可能结果是带有值的布尔值true.默认情况下打印为"1",如果boolalpha设置为true ,则打印为"true" .
| 归档时间: | 
 | 
| 查看次数: | 221 次 | 
| 最近记录: |