Должен ли член быть инициализирован, чтобы получить его адрес? - PullRequest
22 голосов
/ 10 мая 2019

Можно ли инициализировать указатель на элемент данных перед его инициализацией? Другими словами, действительно ли это C ++?

#include <string>

class Klass {
public:
    Klass()
        : ptr_str{&str}
        , str{}
    {}
private:
    std::string *ptr_str;
    std::string str;
};

этот вопрос похож на мой, но там правильный порядок, и ответ говорит:

Я бы посоветовал не кодировать подобное, если кто-то изменит порядок членов в вашем классе.

Что, похоже, означает, что отмена ордера была бы незаконной, но я не был уверен.

Ответы [ 2 ]

25 голосов
/ 10 мая 2019

Нужно ли инициализировать элемент для получения его адреса?

Нет.

Можно ли инициализировать указатель на элемент данных перед инициализациейчлен?Другими словами, действительно ли это C ++?

Да.Да.

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

int a;
int* p1 = &a;

Здесь значение a неинициализировано, и на него можно указывать.

То, что этот пример не демонстрирует, указывает на объект до начала его жизни, что и происходит в вашем примере. Использование указатель на объект до и после его времени жизни явно разрешен, если память занята.Стандартный черновик гласит:

[basic.life] До начала срока службы объекта, но после того, как было выделено хранилище, которое будет занимать объект, или после окончания срока службы объекта и дохранилище, которое занимал объект, используется повторно или освобождается, любой указатель, представляющий адрес места хранения, где объект будет или был расположен, может использоваться, но только ограниченным образом ...

Правило продолжает перечислять, как использование ограничено.Вы можете обойтись со здравым смыслом.Короче говоря, вы можете обращаться с ним так же, как с void*, за исключением того, что нарушение этих ограничений является UB, а не плохо сформировано.Аналогичное правило существует для ссылок.

Существуют также ограничения на вычисление адресов нестатических элементов.Стандартный черновик гласит:

[class.cdtor] ... Чтобы сформировать указатель (или получить доступ к значению) прямого нестатического члена объекта obj, конструкция obj должно начаться, а его уничтожение не должно быть завершено, в противном случае вычисление значения указателя (или доступ к значению элемента) приводит к неопределенному поведению.

В конструкторе Klass,Построение Klass началось, а уничтожение не завершено, поэтому вышеприведенное правило выполнено.


PS Ваш класс копируемый, но копия будет иметь указатель на член другого экземпляра.Подумайте, имеет ли это смысл для вашего класса.Если нет, вам потребуется реализовать пользовательские конструкторы копирования и перемещения и операторы присваивания.Самостоятельная ссылка, подобная этой, является редким случаем, когда вам могут понадобиться пользовательские определения для них, но не пользовательский деструктор, поэтому это исключение из правила пяти (или трех).

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

0 голосов
/ 10 мая 2019

Забавный вопрос.

Это законно и будет "работать", хотя и едва. Есть небольшое «но», связанное с типами, которое делает все это немного неловким с дурным вкусом (но не незаконным) и которое может сделать незаконным некоторые пограничные случаи, связанные с наследованием.

Конечно, вы можете взять адрес любого объекта, независимо от того, инициализирован он или нет, если он существует в области и имеет имя, к которому вы можете добавить operator&. Разыменование указателя - это другое дело, но это был не вопрос.

Теперь тонкая проблема заключается в том, что стандарт определяет результат operator& для нестатических членов структуры как "указатель на член класса C типа T" и является prvalue обозначение C :: m ".

Что в основном означает, что ptr_str{&str} будет принимать адрес str, но типом является не pointer-to , а pointer-to-member- из . Затем он неявно и бесшумно преобразуется в указатель на .

Другими словами, хотя вам не нужно явно писать &this->str, это, тем не менее, его тип - это то, что он есть и что он означает [1] .

Действителен ли this и безопасно ли его использовать в списке инициализаторов? Ну да, просто ... едва . Его можно использовать , если он не используется для прямого или косвенного доступа к неинициализированным элементам или виртуальным функциям . Как, впрочем, и здесь (в другом, возможно, надуманном, случае это может быть не так).

<ч /> [1] Как ни странно, параграф 4 начинается с предложения, в котором говорится, что указатель члена не формируется, когда вы помещаете вещи в скобки. Это замечательно, потому что большинство людей, вероятно, сделали бы это, чтобы быть на 100% уверенными, что они правильно определили приоритет оператора. Но если я правильно читаю, то &this->foo и &(this->foo) ни в коем случае не одинаковы!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...