문제

This question specifically relates to C++98, but feel free to pitch in any useful info w/regard to newer standards if you like.

In case you know the answer and want to skip the rest, the short & sweet of it is:

int **w;
int volatile*     *x = w; // error
int volatile*const*y = w; // OK
int const   *const*z = w; // OK    

Why would const be necessary to the right of volatile in the declaration of y? What possible evil could someone accomplish if the declaration for x were allowed?

In section 4.4.4 of the standard it says:

A conversion can add cv-qualifiers at levels other than the first in multi-level pointers, subject to the following rules:

Two pointer types T1 & T2 are similar if there exists a type T and integer n > 0 such that:

  • T1 is CV10 ptr to CV11 ptr to ... CV1N T
  • T2 is CV20 ptr to CV21 ptr to ... CV2N T

... where each CVij is const, volatile, const volatile, or nothing. The n-tuple of cv-qualifiers after the first in a pointer type, e.g., CV11, CV12, ..., CV1N in the pointer type T1, is called the cv-qualification signature of the pointer type. An expression of type T1 can be converted to type T2 iff the following conditions are satisfied:

  1. the pointer types are similar
  2. for every j > 0, if const is in CV1j, then const is in CV2j, and similarly for volatile.
  3. if the CV1j and CV2j are different, then const is in every CV2k for 0 < k < j

... after which it goes on to give an example for assigning a ** to a const**. The emphasis above is mine, italics are from the document.

Putting this into code:

 int CV13* CV12* CV11* CV10 b1;
 int CV23* CV22* CV21* CV20 b2 = b1;

I'm a little fuzzy on some of the details ... so here's some questions or potentially flawed observations:

1) It says at levels other than the first; this isn't elaborated on any further, but CV20 can be any valid CV qualifier.

2) The 3rd rule at the bottom says if T2 adds either const OR volatile at level j, then levels 1 ... j-1 must be const (or suffer the wrath). In the following, the # of stars differs from the one at the top to emphasize what the 3rd rule says:

int *****w;
int **volatile*     *     *x = w; // error
int **volatile*const*const*y = w; // OK
int **const   *const*const*z = w; // OK

I understand why it's needed for z, but why with y? Here's roughly what the example in 4.4.4 looks like, modified for the volatile case:

void f( int **x ) {
  int volatile**y = x; // not allowed
  // do some evil here
}

What evil could be put there?

도움이 되었습니까?

해결책

(Note: "This question specifically relates to C++98" but the state of affairs is the same in all versions of the Standard, past and present (and future too I would bet), because it's essentially about const-correctness and preventing coders from opening a hole in the type system.)

As the Standard uses the general term "cv-qualifiers", I find it easier to understand when reasoning only with "const" (no "volatile") [but see further below for an example with volatile]. The "C++ FAQ Lite" has a related entry : Why am I getting an error converting a Foo**Foo const**?

Essentially, allowing the conversion would let you silently modify a const T (through a pointer to non-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!

But by adding a "first-level" const, the conversion int const* const* pcpc = pp; is valid because the compiler will prevent you doing *pcpc = &theAnswer; afterwards (because *pcpc is const).


Edit: As for volatile, the problem is maybe less obvious than with const, but allowing the conversion would let you silently incorrectly access (read or write) a volatile T as if it were not volatile (through a pointer to non-volatile T):

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

But by adding a "first-level" const, the conversion int volatile* const* pcpv = pp; is valid because the compiler will prevent you doing *pcpv = &externTimer; afterwards (because *pcpv is const).

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top