C++ block scope extern declaration linkage, confus

2019-06-16 09:46发布

问题:

Standards N3242 (C++ 11 draft) and N3797 (C++ 14 draft) both have the same paragraph.

§ 3.5 Program and linkage [basic.link]

¶ 6

The name of a function declared in block scope and the name of a variable declared by a block scope extern declaration have linkage. If there is a visible declaration of an entity with linkage having the same name and type, ignoring entities declared outside the innermost enclosing namespace scope, the block scope declaration declares that same entity and receives the linkage of the previous declaration. If there is more than one such matching entity, the program is ill-formed. Otherwise, if no matching entity is found, the block scope entity receives external linkage. [ Example:

 static void f();
 static int i = 0; // #1
 void g() {
     extern void f(); // internal linkage
     int i; // #2 i has no linkage
     {
         extern void f(); // internal linkage
         extern int i; // #3 external linkage
     }
 }

There are three objects named i in this program. The object with internal linkage introduced by the declaration in global scope (line #1), the object with automatic storage duration and no linkage introduced by the declaration on line #2 , and the object with static storage duration and external linkage introduced by the declaration on line #3. — end example ]

I feel something is wrong with the i object example, it does not support what is said in the paragraph before, right? In my opinion it must be two i objects, one with internal linkage (#1 and #3) and one with no linkage (#2). Am I correct? Is it a mistake in the standard, the example is wrong?

In contrast, standard N4659 (C++ 17 draft) put it more correctly, in my opinion.

§ 6.5 Program and linkage [basic.link]

¶ 6

The name of a function declared in block scope and the name of a variable declared by a block scope extern declaration have linkage. If there is a visible declaration of an entity with linkage having the same name and type, ignoring entities declared outside the innermost enclosing namespace scope, the block scope declaration declares that same entity and receives the linkage of the previous declaration. If there is more than one such matching entity, the program is ill-formed. Otherwise, if no matching entity is found, the block scope entity receives external linkage. If, within a translation unit, the same entity is declared with both internal and external linkage, the program is ill-formed. [ Example:

static void f();
static int i = 0; // #1
void g() {
    extern void f();
    // internal linkage
    int i; // #2: i has no linkage
    {
        extern void f(); // internal linkage
        extern int i; // #3: external linkage, ill-formed
    }
}

Without the declaration at line #2, the declaration at line #3 would link with the declaration at line #1. Because the declaration with internal linkage is hidden, however, #3 is given external linkage, making the program ill-formed. — end example ]

Some testing

static void f();
static int i = 10; // #1
void g() {
  extern void f(); // internal linkage
  std::cout << i << std::endl;
  int i = 2; // #2 i has no linkage
  std::cout << i << std::endl;
  {
    extern void f(); // internal linkage
    std::cout << i << std::endl;
    extern int i; // #3 external linkage
    std::cout << i << std::endl;
  }
}

int main() {
  g();
  return 0;
}

This code produces 10 2 2 10 in clang-5.0 with any of -std value from c++-11, c++-14, c++17. What basically support the wording of C++ 11 and 14 drafts. Apparently, this did not change to a compilation error with c++17 value. Is clang not compliant with C++ 17 on this point?

回答1:

That was issue 426:

Is this really what we want? C99 has 6.2.2.7/7, which gives undefined behavior for having an identifier appear with internal and external linkage in the same translation unit. C++ doesn't seem to have an equivalent.

[…] The CWG decided that it would be better to make a program with this kind of linkage mismatch ill-formed instead of having undefined behavior.

That is, it was decided that if a name has linkage, it shall be consistent. Implementations probably haven't implemented this yet.