Этот вопрос конкретно относится к C ++ 98, но не стесняйтесь вносить любую полезную информацию относительно новых стандартов, если хотите.
Если вы знаете ответ и хотите пропустить остальное, короткое & сладкое из этого:
int **w;
int volatile* *x = w; // error
int volatile*const*y = w; // OK
int const *const*z = w; // OK
Почему бы const
быть необходимым справа от volatile
в декларации y
? Какое возможное зло может совершить кто-то, если декларация x
были разрешены?
В разделе 4.4.4 стандарта говорится:
Конверсия может добавить cv-квалификаторы на уровнях, отличных от первого в многоуровневых указателях действуют следующие правила:
Два типа указателя T1 & Т2 являются аналогичный если существует тип T и целое число N > 0 такое что:
- T1 — от CV10 до CV11 от … до CV1N T
- T2 — от CV20 до CV21 от … до CV2N T
… где каждый CVij является const, volatile, const volatile или ничего. N-кортеж cv-квалификаторов после первый тип указателя, например, CV11, CV12, …, CV1N в типе указателя T1, называется квалификационная подпись типа указателя. Выражение типа T1 может быть преобразовано в тип T2 тогда и только тогда выполняются следующие условия:
- типы указателей похожи
- для каждого J > 0, если const находится в CV1j, то const находится в CV2j, и аналогично для volatile.
- если CV1j и CV2j разные, то const есть в каждом CV2k для 0 < К < J
… после чего он продолжает приводить пример для назначения **
к const**
, акцент выше мое, курсив из документа.
Поместив это в код:
int CV13* CV12* CV11* CV10 b1;
int CV23* CV22* CV21* CV20 b2 = b1;
Я немного нечеткий в некоторых деталях … так что вот некоторые вопросы или потенциально ошибочные наблюдения:
1) это говорит at levels other than the first
; это не уточняется дальше, но CV20
может быть любым действительным классификатором CV.
2) 3-е правило внизу говорит, добавляет ли T2 либо const
ИЛИ ЖЕ volatile
на уровне j
тогда уровни 1 ... j-1
должен быть постоянным (или терпеть гнев). Далее количество звездочек отличается от числа наверху, чтобы подчеркнуть то, что говорит 3-е правило:
int *****w;
int **volatile* * *x = w; // error
int **volatile*const*const*y = w; // OK
int **const *const*const*z = w; // OK
Я понимаю зачем это нужно для z
но почему с y
? Вот пример того, как выглядит пример в 4.4.4, модифицированный для изменчивого случая:
void f( int **x ) {
int volatile**y = x; // not allowed
// do some evil here
}
Какое зло может быть помещено туда?
(Примечание: «Этот вопрос конкретно относится к C ++ 98», но положение дел одинаково во всех версиях Стандарта, в прошлом и настоящем (и в будущем, я бы даже поспорил), потому что в основном это касается правильности и предотвращения констант). кодеры от открытия дыры в системе типов.)
В качестве стандарта используется общий термин «CV-классификаторы«Мне легче понять, если рассуждать только с помощью»const
«(нет»volatile
«) [но см. ниже ниже пример с volatile
]. «C ++ FAQ Lite» имеет связанную запись: Почему я получаю ошибку при конвертации Foo**
→ Foo const**
?
По сути, разрешение преобразования позволит вам молча изменить Const T (через указатель на не-const T):
int const theAnswer = 42;
int* p = 0; // int* p = &theAnswer; is not allowed of course...
int** pp = &p;
int const** ppc = pp; // <-- Error, but imagine this were allowed...
*ppc = &theAnswer; // &theAnswer is `int const*` and *ppc is `int const*` too,
// but it's also doing *pp = &theAnswer; i.e. p = &theAnswer;
*p = 999; // I.e. theAnswer = 999; => modifying a const int!
Но, добавив «первый уровень» const
конверсия int const* const* pcpc = pp;
допустимо, потому что компилятор будет препятствовать вам делать *pcpc = &theAnswer;
потом (потому что *pcpc
является const
).
Редактировать: Что касается volatile
, проблема, возможно, менее очевидна, чем с const
, но разрешение на преобразование позволит вам молча неверно получить доступ (чтение или запись) к летучий Т, как будто это было не изменчивый (через указатель на не-летучий Т):
extern int volatile externTimer;
int* p = 0; // int* p = &externTimer; is not allowed...
int** pp = &p;
int volatile** ppv = pp; // <-- Error, but imagine this were allowed...
*ppv = &externTimer; // &externTimer is `int volatile*` and *ppv too,
// but it's also doing *pp = &externTimer; i.e. p = &externTimer;
int a1 = externTimer; // First read
int a2 = externTimer; // Second read, mandatory: the value may have changed externally
int b1 = *p; // First read
int b2 = *p; // Second read? may be optimized out! because *p is not volatile
Но, добавив «первый уровень» const
конверсия int volatile* const* pcpv = pp;
допустимо, потому что компилятор будет препятствовать вам делать *pcpv = &externTimer;
потом (потому что *pcpv
является const
).
Других решений пока нет …