Безопасно ли получить доступ к неконстантному объекту через const lvalue? - PullRequest
2 голосов
/ 19 марта 2019

6.5(p7) гласит:

Доступ к сохраненному значению объекта должен осуществляться только через выражение lvalue одного из следующих типов: 88)

- aтип, совместимый с действующим типом объекта,

- квалифицированная версия типа, совместимого с эффективным типом объекта,

- тип, который является типом со знаком или без знака, соответствующимДействительный тип объекта,

- тип, являющийся типом со знаком или без знака, соответствующий квалифицированной версии действующего типа объекта,

Взгляд на 6.7.3(p10):

Для совместимости двух квалифицированных типов оба должны иметь идентично квалифицированную версию совместимого типа;

, поэтому double и const double не являютсясовместимы, потому что их квалификаторы типов не совпадают.

Теперь я предположил, что тип со знаком или без знака означает целочисленный тип со знаком или без знака , как это определено 6.2.5(p4) и 6.2.5(p6) соответственно, поскольку 6.2.5 не определяет тип со знаком сам по себе.

Теперь рассмотрим следующий код:

void print_double(const double d){
    printf("d = %lf\n", d);
}

int main(int args, const char *argv[]){
    double d = 10.2;
    print_double(d);
}

Теперь я пытаюсь применить 6.5 (p7):

I.a type compatible the effective type of the object

Нет, const double и double не совместимы.

II.a qualified version of a type compatible with the effective type of the object

Нет, квалифицированная версия const double сделает ее еще более квалифицированной, чем const double

III / IV.

Нет, double нетцелочисленный тип со знаком или без знака.

Это суждение кажется ошибочным, поскольку для доступа к неконстантному объекту должно быть приемлемо использование lvalue с определением const.Но я не могу вывести это из 6.5(p7).

Ответы [ 2 ]

7 голосов
/ 19 марта 2019

Теперь рассмотрим следующий код:

void print_double(const double d){
    printf("d = %lf\n", d);
}

int main(int args, const char *argv[]){
    double d = 10.2;
    print_double(d);
}

Ваш код не имеет псевдонимов. d в main и d in print_double являются различными объектами, поскольку аргументы всегда передаются по значению. Но если бы мы «исправили» пример:

void print_double(const double *pd){
    printf("d = %lf\n", *pd);
}

int main(int args, const char *argv[]){
    double d = 10.2;
    print_double(&d);
}

Это все еще четко определено. *pd является lvalue типа const double. const double является квалифицированной версией double. Все типы совместимы между собой (тривиально). Таким образом, вторая пуля имеет место, и этот псевдоним действителен.

2 голосов
/ 19 марта 2019

Если два указателя (lvalue доступы) могут псевдоним , это другой вопрос, чем если бы они указывали на совместимые типы . Часть, которую вы цитируете, касается псевдонимов указателей (иначе говоря, «строгое правило псевдонимов»), которая по какой-то причине записывается как:

  • тип, совместимый с эффективным типом объекта,
  • квалифицированная версия типа, совместимого с эффективным типом объекта

Это означает, что объект с квалификацией const может псевдоним не квалифицированным объектом, даже если они не являются совместимыми типами. Пример:

void f (const int* a, int* b);

a может указывать на те же данные, что и b. Компилятор не вправе предполагать, что они указывают на отдельные объекты.

Но это даже не относится к вашему случаю, поскольку вы передаете параметр по значению и создаете локальную копию. Псевдоним будет применяться, если ваш пример был такой:

double a = 10.2;

int main(int args, const char *argv[]){
    print_double(&a);
}

void print_double(const double* b){
    printf("d = %lf\n", b);
}

Где параметр b - это квалифицированная версия типа, совместимого с эффективным типом объекта (double). Поэтому компилятор может не предполагать, что b не указывает на глобальную переменную a.


По поводу совместимости:

Всякий раз, когда вы копируете объект либо с помощью присваивания =, либо путем передачи его в качестве параметра функции, правила простого присвоения включаются. 6.5.16.1 позволяет это:

  • левый операнд имеет атомарный, квалифицированный или неквалифицированный тип указателя и (учитывая тип, который левый операнд будет иметь после преобразования lvalue) оба операнда указатели на квалифицированные или неквалифицированные версии совместимых типов, а тип указывает слева находятся все квалификаторы того типа, на который указывает справа;

Значение int* a; const int* b = a; в порядке, но const int* a; int* b = a; является нарушением ограничения. При передаче параметров применяются те же правила, см. 6.9.1 / 10:

При входе в функцию выражения размера каждого изменяемого параметра оценивается, и значение каждого выражения аргумента преобразуется в тип соответствующий параметр как бы по присваиванию.

В вашем случае происходит то, что значение d в вызывающей стороне копируется в новую переменную типа const double. Это разрешено по правилу простого присваивания, и вы создаете новую переменную - все хорошо.

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