此问题是由clang和gcc针对nullptr
检查指针值的处理不当引起的。对于this
,它们都发出警告,但是对于在对象上使用address-of
运算符获取的指针,它们保持安静。
我非常确定这样的指针应该一直有效,因为现代编译器从快乐的90年代实际上取消了对c ++代码的检查以来,我们已经遇到了错误。
令我感到困惑的是,为什么编译器在一般情况下保持安静。 if
是否有可能被触发,或者仅仅是两个主要编译器中的设计决定?在我开始编写补丁或调试编译器开发人员之前,我想确定一下我没有错过什么。
#include <iostream>
class A {
void f(){
if(!this) {
std::cout << "This can't trigger, and compilers warn about it.";
}
}
};
void f(A& a){
A* ptr = &a;
if(ptr == nullptr) {
std::cout << "Can this trigger? Because gcc and clang are silent.";
}
}
尽管这个问题看起来很愚蠢,但我发现它很实用。如果确实使用臭代码,则此优化将导致致命的结果,因此警告将是非常有用的诊断。
补充情况。 clang和gcc都知道检查具有不变的评估能力,因为即使对于干净的代码:
void g(A* a){
A* ptr = a;
if(ptr == nullptr) {
std::cout << "Gee, can this trigger? Be cause gcc and clang are silent.";
}
}
void g(A& a) {
g(&a);
}
它们生成g
的两个版本,if
中省略了g(A& a)
,因此两者都可以确定并假定不可为空,以供参考。 gcc生成易于阅读的汇编:
f(A&):
ret
.LC0:
.string "Can this trigger? Be cause gcc and clang are silent."
g(A*):
test rdi, rdi
je .L5
ret
.L5:
mov edx, 52
mov esi, OFFSET FLAT:.LC0
mov edi, OFFSET FLAT:_ZSt4cout
jmp std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
g(A&):
ret
据我了解,程序集msvc /O2
和icc -fast
将检查留在原处。
编辑:我错过了!
中的A::f()
,已将其修复。
答案 0 :(得分:5)
从引用中获取的指针在定义良好的c ++中是否可以为null?
不。此答案中的标准引号:Is null reference possible?
尽管如此,在特定情况下,使用重载的类类型的operator&
获取指针可以返回任何内容,包括null。
if
是否有可能触发?
不在A::f
或::f
中。可以在g(A*)
中触发,但不能从g(A&)
调用时触发。
警告将是非常有用的诊断。
如您所见,GCC和Clang不够聪明,无法在那种情况下检测到错误,但是它们确实可以检测到同一错误的简单版本:
海湾合作委员会
warning: the compiler can assume that the address of 'a' will never be NULL [-Waddress] if(&a == nullptr) { ~~~^~~~~~~~~~ warning: nonnull argument 'a' compared to NULL [-Wnonnull-compare] if(&a == nullptr) { ^~
C
warning: reference cannot be bound to dereferenced null pointer in well-defined C++ code; comparison may be assumed to always evaluate to false [-Wtautological-undefined-compare] if(&a == nullptr) {