这个问题是由于Clang和GCC对nullptr
检查指针值的不均匀处理而动机。对于this
,它们都发出警告,但对于通过在对象上使用address-of
操作员来保持安静的指针。
我很确定,这样的指针应该始终有效,因为我们由于现代编译器从Happy 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.";
}
}
即使问题看起来很愚蠢,我也觉得这很实际。如果一个人确实可以使用Smelly代码,则此优化的结果是致命的,因此警告将是一个非常有用的诊断。
补充此案。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(A& a)
中省略if
的g
的两个版本,因此两者都可以确定并假定参考的非删除性。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()
中的!
,修复了它。
可以在明确定义的C ?
否。此答案中的标准报价:无效参考?
虽然,尤其是使用类型的过载的operator&
服用指针的情况,可以返回任何内容,包括null。
if
是否有可能触发?
不在A::f
和::f
中。可以在g(A*)
中触发,但在g(A&)
调用时不会触发。
警告将是一个非常有用的诊断。
GCC和Clang不够聪明,无法在您观察到的那种情况下检测出错误,但是它们确实检测到了同一错误的更简单版本:
GCC
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) { ^~
clang
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) {