Я работаю над классом, в котором я пытаюсь использовать постоянные ссылочные члены, которые ссылаются на другие переменные-члены, чтобы обеспечить открытый постоянный доступ к этим членам. Есть ли законный способ сделать это?

Код компилируется, но выдает предупреждения о привязке ссылочного элемента к временным значениям. После вызова resize() A все еще указывает на ноль, хотя myA обновляется.

Ниже приведено описание того, что я пытаюсь сделать. Я думаю, что ссылка на себя является незаконной и приводит к проблеме, но я не уверен. Любой вклад будет высоко ценится.

class MyClass{
    private:
    int mySize;
    double * myA;

    void resize(int inSize) {
        mySize = inSize;

        delete [] myA;

        // detection for non-positive size is not included for brevity
        myA = new double [size];
    }

    public:
    const int & size;
    const double * const & A;

    MyClass(): 
    mySize(0)   , myA(nullptr)
    size(mySize), A(myA)      {}

    // dtor is not included
};

int main(){
    MyClass myObj;

    myObj.resize(42);

    // myObj.A[1] = 0.5 // seg fault
}

РЕДАКТИРОВАТЬ: Майкл указал, что эта проблема связана с квалификаторами const A, и я подтвердил это, изменив квалификаторы A, получив следующий результат:

double * &A -> A правильно связывается с myA.

double * const &A -> A правильно связывается с myA.

const double * &A -> Несовместимо. Ошибка компилятора.

const double * const &A -> A привязывается к временной переменной.

РЕДАКТИРОВАТЬ: Кажется, что лучшая практика заключается в использовании функции получения.

3
Yifan Lai 12 Апр 2019 в 02:14

2 ответа

Лучший ответ

Типы double* и const double* несовместимы, поэтому вы не можете инициализировать ссылку на последний из экземпляра первого. Однако это не является недопустимым, а компилятор вместо этого генерирует временную привязку к ссылке (что приводит к зависанию ссылки - ИМХО, это не должно быть разрешено, то есть будет ошибкой).

Если вы намеревались предоставить public const доступ к вашим private данным, тогда стандартным методом является использование функций-членов:

int size() const noexcept { return mySize; }
const double* A() const noexcept { return myA; }

Который будет оптимизирован компилятором.

Еще один способ заставить ваш код работать, это добавить еще одного частного члена:

double*myA;
const double*myAconst;

const double* const& A = myAconst;

И убедитесь, что myA и myAconst всегда указывают на один и тот же адрес. Вы также можете отказаться от myA в пользу myAconst и использовать const_cast<double*>(myAconst), если вам нужно изменить выделенные элементы (это законное использование const_cast<>, поскольку адрес указывает на подлинные непостоянные данные.

1
Walter 12 Апр 2019 в 01:12

Проблема с инициализацией A(myA) состоит в том, что A имеет тип const double * const &, а myA имеет тип double *. Тип, на который ссылается A, не является справочно-совместимым с типом myA (спасибо @guest за разъяснение этого). В результате ссылка A будет привязана к временному объекту, материализованному из значения, которое является результатом неявного преобразования myA в const double * const [dcl.init.ref] /5.4.2. Однако срок действия временного не будет продлен [class.tevent] / 5 и, таким образом, временный объект будет уничтожен сразу после инициализации, оставив A оборванных. Об этом вас предупреждает компилятор.

Нет ничего противозаконного в том, чтобы ссылаться на своих участников. Вопрос в том, какой смысл это делать? Хотя ссылки сами по себе не являются объектами, и неясно, занимают ли они память или нет [dcl. ref] / 4, компиляторы, как правило, реализуют ссылочные переменные-члены, вводя поля, которые содержат адреса объектов, на которые ссылается ссылка (другими словами: ссылки будут компилироваться в основном в указатели). В результате половина памяти, используемой объектом типа MyClass в приведенном выше примере, будет использоваться для хранения адресов, которые указывают на объект, в котором хранятся сами адреса. Знание, где найти один из этих адресов, требует знания того, на что они указывают… Так что это не незаконно, но, вероятно, это не такая уж хорошая вещь…

4
Michael Kenzel 12 Апр 2019 в 02:00