Может ли указатель, взятый из ссылки, быть нулевым в четко определенном c ++? - PullRequest
4 голосов
/ 25 июня 2019

Этот вопрос мотивирован неравномерной обработкой проверки значения указателя по nullptr clang и gcc. Для this они оба выдают предупреждение, но для указателя, взятого с помощью оператора address-of на объект, они хранят молчание.

Я почти уверен, что такой указатель всегда должен быть действительным, потому что мы столкнулись с ошибками из-за того, что современный компилятор удалил такие проверки кода c ++ из счастливых 90-х годов, когда он действительно срабатывал.

Меня беспокоит, почему компиляторы молчат в общем случае. Возможно ли как-то запустить 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(), исправил.

1 Ответ

5 голосов
/ 25 июня 2019

Может ли указатель, взятый из ссылки, быть нулевым в четко определенном c ++?

Нет.Стандартные кавычки в этом ответе: Возможна ли нулевая ссылка?

Хотя в конкретном случае взятия указателя с использованием перегруженного operator& типа класса может возвращаться что угодно, включая ноль.

Возможно ли сработать 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) {
...