C++ 它是否总是有效的C++;是否要将成员函数指针与0进行比较?

C++ 它是否总是有效的C++;是否要将成员函数指针与0进行比较?,c++,function,pointers,member,virtual-functions,C++,Function,Pointers,Member,Virtual Functions,在以下代码中,断言不是在Visual Studio编译器中引发的,而是在使用XCode为iPhone编译时引发的: class X { public: virtual void A() {} }; X x; void main() { // Define a valid member function pointer to X::A. void (X::*p)() = &X::A; assert(p != 0); } 这是编译器中的错误吗?在这种情况下,还有什么方法可以

在以下代码中,断言不是在Visual Studio编译器中引发的,而是在使用XCode为iPhone编译时引发的:

class X
{
public:

 virtual void A() {}
};

X x;

void main()
{
 // Define a valid member function pointer to X::A.
 void (X::*p)() = &X::A;

 assert(p != 0);
}

这是编译器中的错误吗?在这种情况下,还有什么方法可以检查空指针呢?

代码是正确的,编译器不符合标准,即(第节
[expr.eq]
,使用C++0x草案n3225中的措辞,但在其他版本中应该相同):

任何指向成员的指针都可以与空指针常量进行比较

如果两个操作数都为空,则它们的比较结果相等。否则,如果只有一个为空,则它们比较不相等

空指针常数的相关定义(部分
[conv.ptr]
std::nullptr\t
部分在C++0x中是新的):

空指针常量是整数类型的整数常量表达式(5.19)prvalue,其计算结果为零或类型为
std::nullptr\u t

和(第节
[expr.const]
):

如果常量表达式是整型或枚举类型,则它是整型常量表达式

注意:顺便说一句,指向虚拟成员函数的指针的实现定义的表示形式通常是虚拟表的索引,问题中的索引为0。但是根据标准,断言中的表达式并没有检查表示是否为零,而是检查零文本,这是一个空指针检查

苹果的编译器显然把这两者混淆了。如果要测试表示是否为零,可以编写
assert((intptr\u t)p!=0)
——这将是完全不可移植的

但是编写的代码是完全可移植的,可以移植到任何符合标准的编译器,并且永远不会断言

编辑:还有一条引用自《标准》,它只是重复了我们已经学到的内容(部分
[conv.mem]
):

空指针常量(4.10)可以转换为指向成员类型的指针;结果是该类型的空成员指针值,可与任何指向不是由空指针常量创建的成员的指针区分开来


iPhone编译器出错了

请注意,指向虚拟函数的成员函数指针之间的相等性比较会产生未指定的结果,即此断言

assert(&X::A == &X::A);
从形式上看,行为不可预测


但是成员函数指针与空指针常量的比较是由语言严格定义的。在您的示例中,指针不能为null,因此它不应与null指针常量进行比较。

@Mahesh:我认为这并不能回答他的问题。他并不是说在为iPhone编译时,
assert
导致他的程序终止。他问为什么断言不是在Visual Studio下提出的。@Mahesh:问题不在于断言的行为,而是编译器错误地计算了正在测试的表达式<代码>p!=0必须根据标准计算为true。
main
从不返回
void
,始终
int
@Merlyn:
assert(true)
在调试和发布模式下都是不可操作的。内部表达式为true,由standard.NULL保证。但是,在OPs代码中,或者在标准的引用部分,NULL都没有出现——该标准明确声明,计算结果为零的整型常量(在OP的断言中使用)将转换为NULL指针常量,这就是标准所指的。不是预处理器宏NULL。@ GMM:我知道这是在每个编译器中有效发生的事情,但是它实际上是在C++标准中定义的吗?<代码> null=0</代码>?@兰伯特:是的,这就是每个人都想告诉你的。我在编辑后的答案中引用了标准中的相关定义。@Lambert:C版本(例如
((void*)0)
)在可变长度参数列表中的作用可能不同。但这是不可移植的,因为
NULL
通常被定义为简单的
0
。许多C++专家,包括StruouStru-Irc,都只使用<代码> 0代码>,放弃<代码> null <代码>宏。@兰伯特:Gman没有对C++ 0x说什么。我确实这样做了,而且我还特别告诉您C++0x在哪里更改了某些内容(新的
std::nullptr\t
类型)。