Озадаченное введение временного в `* const &` на старых компиляторах C ++ - PullRequest
2 голосов
/ 21 октября 2019

При сборке на платформе со старым компилятором C ++ я заметил неожиданное поведение с кодом, который отлично работал в других местах. Я не уверен, указывает ли это на ошибку в старых компиляторах или некоторые изменения в стандарте, но я явно собирал с помощью C ++ 11.

В частности, старый компилятор, кажется, использует временныйдля * const&, когда я этого не ожидал, и это временно приводит к SIGSEGV, когда его стековый фрейм очищается и перезаписывается.

Вот мои лучшие усилия по выделению MWE из исходного кода,Моя проблема вращается вокруг конструктора класса C:

#include <stdio.h>

struct A {
    int* i;
};

class B {
public:
    int* const& i_ptr_const_ref; // normally not public
    B(int* const& i_ptr) : i_ptr_const_ref(i_ptr) {}
    operator int* const& (void) const { return i_ptr_const_ref; }
};

int null_b = { 0 };
int* null_b_ptr = &null_b;
int* const& null_b_ptr_const_ref = null_b_ptr;

class C {
public:
    B b;
//  C(const A* a_ptr) : b(a_ptr ? a_ptr->i : null_b_ptr_const_ref) {} // this works
    C(A* a_ptr) : b(a_ptr ? a_ptr->i : null_b_ptr_const_ref) {} // this fails
//  C(A* a_ptr) : b(a_ptr ? (int* const&) a_ptr->i : null_b_ptr_const_ref) {} // this works
//  C(A* a_ptr) : b(a_ptr->i) {} // this works
};

int main(void) {
    A a;
    A* a_ptr = &a;
    a_ptr->i = (int*) 42;
    C c(a_ptr);
    printf("c.b.i_ptr_const_ref = %p\n", (void*) c.b.i_ptr_const_ref);
    printf("c.b=                  %p\n", (void*) c.b);
    printf("a_ptr->i=             %p\n", (void*) a_ptr->i);
    return 0;
}

Попробуйте это в Проводнике Компилятора

Все значения, которые я распечатаю, должны совпадать, но наКомпиляторы GCC до 5.1 и ICC до 18 (я понимаю, что Intel гордится совместимостью «ошибка за ошибку» с другими компиляторами), средний показывает адрес стека вместо ожидаемого значения. Все версии компиляторов Clang и ELLCC, которые я смог опробовать, ведут себя корректно.

Я хочу использовать конструктор C без комментариев, но он работает неправильно. Я получаю ожидаемый результат в MWE, если я задаю параметр A* a_ptr const, но в большей базе кода я не могу этого сделать. Я также получаю ожидаемый результат, если я не использую ?: в инициализаторе, или если я явно приведу a_ptr->i в инициализаторе как int* const&, но я не понимаю, почему я должен это делать.

Я бы подумал, что инициализация int* const& с int* будет в порядке, но я думаю, что ?: каким-то образом запутал компилятор. Кто-нибудь может мне помочь понять, являются ли здесь более старые компиляторы GCC и ICC неправильными, или есть что-то в языке, который я неправильно понимаю?

1 Ответ

1 голос
/ 22 октября 2019

Это довольно явно ошибка компилятора: конечно, lvalue типа int* может быть неявно преобразовано в int* const&, поэтому результатом условного оператора должно быть lvalue.

printfвывод кажется загадочным, но на самом деле это прямое следствие ошибки: даже first read of c.b.i_ptr_const_ref (что, конечно, является ссылкой, которая прозрачно соблюдается) читает из мертвого временного, но этоеще не перезаписано и все еще содержит копию из a.i. После первого printf эта память была захвачена и вместо нее хранится адрес стека.

...