Почему указатель типа "struct with const member" не может указывать на "struct with non-const member"? - PullRequest
0 голосов
/ 05 октября 2018

Этот код не компилируется:

struct s_t {
    int a;
};

struct c_s_t {
    const int a;
};

s_t s;
c_s_t *c_s = &s;
ibug@ubuntu:~ $ g++ -fsyntax-only t.cpp
t.cpp:10:15: error: cannot convert ‘s_t*’ to ‘c_s_t*’ in initialization
 c_s_t *c_s = &s;
               ^

Однако этот код отлично компилируется:

int a, *pa = &a, **ppa = &pa, ***pppa = &ppa;
const int * const * const * const cpcpcpa = pppa;

Я понимаю, что указатель более квалифицирован CV на любой уровень может указывать на менее квалифицированный CV объект на любом уровне, но почему это не то же самое для структур?


Вышеприведенная формулировка проблемы является MCVE болеесложная проблема, когда мой друг пытался преобразовать указатели между t_s_t<T> и t_s_t<const T>, где t_s_t - это тип структуры шаблона с одним параметром шаблона typename, а T - произвольный тип.

Ответы [ 4 ]

0 голосов
/ 05 октября 2018

Я понимаю, что указатель, более квалифицированный CV на любом уровне, может указывать на менее квалифицированный CV объект на любом уровне

Это не совсем так, по крайней мерене так, как вы описали.Только самый верхний CV-квалификатор может быть добавлен произвольно (и, конечно, CV-квалификатор на самом указателе!), Как в случае C и C ++.

Вот контрпример дляПонятие «любой уровень», взятое прямо из [conv.qual/3] в текущем проекте стандарта:

[Примечание: Если программа может назначить указатель типа T** науказатель типа const T** (то есть, если разрешена строка # 1 ниже), программа может непреднамеренно изменить объект const (как это делается в строке # 2).Например,

int main() {
  const char c = 'c';
  char* pc;
  const char** pcc = &pc;       // #1: not allowed
  *pcc = &c;
  *pc = 'C';                    // #2: modifies a const object
}

- конечная нота]

В любом случае, тогда вы спрашиваете:

но почему нет?То же самое для структур?

Конечно, вы можете указать const T* на T, но это не то, что вы делаете.Это правило не применяется рекурсивно.Классы могут содержать более одного члена, поэтому ваш подход просто не работает в целом (и нет необходимости в специальном правиле для классов, состоящих из одного члена).

В этом конкретном случае два класса являются макетамисовместим, поэтому я ожидаю, что reinterpret_cast будет работать в большинстве случаев:

struct s_t {
    int a;
};

struct c_s_t {
    const int a;
};

int main()
{
   s_t s;
   c_s_t *c_s = reinterpret_cast<c_s_t*>(&s);
}

( live demo )

Однако, похоже, что алиасы по достоинствам совместимости с макетом на самом деле не определены четко , так что в конечном итоге вам лучше переосмыслить свой дизайн.

tl; dr: Различные типы - это разные типы.

0 голосов
/ 05 октября 2018

Причина в том, что s_t и c_s_t - это разные типы.

Даже если вы определите c_s_t как:

struct c_s_t {
    int a; // <-- non-const!
};

Тогда:

s_t s;
c_s_t *c_s = &s;

Это все еще не будет работать.

0 голосов
/ 05 октября 2018

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

Это будет работать:

s_t s;
s_t *c_s = &s; //types are matching
0 голосов
/ 05 октября 2018

Эти две структуры разные, и они не конвертируемы.Тот факт, что они имеют одинаковые члены, не имеет значения, и даже если вы удалите const из c_s_t, это ничего не изменит.

Ваш другой пример работает, потому что вы применяете модификаторы для одного типа,Например, это совершенно законно:

struct s_t {
    int a;
};

s_t s;
const s_t const* cs = &s;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...