在定义明确的c ++中,从引用获取的指针是否可以为null?

luk*_*k32 4 c++ pointers reference null-pointer language-lawyer

这个问题是由于检查指针值nullptr受到clang和gcc 的不均衡处理而引起的。因为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.";
    }
}
Run Code Online (Sandbox Code Playgroud)

尽管这个问题看起来很愚蠢,但我发现它很实用。如果使用臭代码工作,则此优化将导致致命的结果,因此警告将是非常有用的诊断。

以补充情况。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);
}
Run Code Online (Sandbox Code Playgroud)

它们产生的两个版本gif在省略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
Run Code Online (Sandbox Code Playgroud)

据我了解组装,msvc /O2icc -fast保留检查到位。

编辑:我错过!A::f(),修复它。

eer*_*ika 5

Can pointer taken from reference ever be null in well-defined c++?

No. Standard quotes in this answer: Is null reference possible?

Although, in particular case of taking the pointer using an overloaded operator& of a class type can return anything, including null.

Is it somehow possible for the if to trigger?

Not in A::f nor ::f. It is possible to trigger in g(A*) but not when called from g(A&).

a warning would be a really useful diagnostic.

GCC nor Clang are not smart enough to detect the mistake in that case as you've observed, but they do detect a simpler version of the same mistake:

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) {
     ^~
Run Code Online (Sandbox Code Playgroud)

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) {
Run Code Online (Sandbox Code Playgroud)