19

Is the following program well-formed or ill-formed according to the c++ standard?

namespace X { int i; }

namespace Y { using X::i; }

int main() { using X::i; using Y::i; }

I'm getting different results with different compilers:

I don't want to fix this program to make it compile on GCC. I just want to know what the c++ standard says about this and why the three compilers behave differently. Also I want to if this is a result of a bug in any of these compilers.

Supremum
  • 542
  • 7
  • 23

2 Answers2

12

The program should not compile because it declares X::i twice in the same block scope.

C++14 §7.3.3/10:

A using-declaration is a declaration and can therefore be used repeatedly where (and only where) multiple declarations are allowed. [Example:

namespace A {
    int i;
}

namespace A1 {
    using A::i;
    using A::i;           // OK: double declaration
}

void f() {
    using A::i;
    using A::i;           // error: double declaration
}

Edit: The non-normative comment quoted above, and which I thought answered the question, was there originally in C++98 and has survived through Technical Corrigendum 1 (C++03), C++11 and C++14. But apparently it's wrong. Richard Smith in his answer cites core issue 36 about it, first raised by Andrew Koenig on 2nd August 1998 (less than a month after ANSI approval of the first standard), which apparently means that a known incorrect comment can survive three revisions of the standard.

Citing the core issue itself about that:

C++ Standard Core Language Active Issues, issue 36:

Notes from 04/00 meeting:
The core language working group was unable to come to consensus over what kind of declaration a using-declaration should emulate. In a straw poll, 7 members favored allowing using-declarations wherever a non-definition declaration could appear, while 4 preferred to allow multiple using-eclarations only in namespace scope (the rationale being that the permission for multiple using-declarations is primarily to support its use in multiple header files, which are seldom included anywhere other than namespace scope). John Spicer pointed out that friend declarations can appear multiple times in class scope and asked if using-declarations would have the same property under the "like a declaration" resolution.

As a result of the lack of agreement, the issue was returned to "open" status.

The general discussion of multiple declarations of the same name is in §3.3.1/4 in both C++98 and C++14. As far as I can see the C++14 text is verbatim identical to the original C++98 text. And by itself it allows declaring the same name multiple times in the same declarative region in a number of cases, one of which is that all the declarations refer to the same entity:

C++14 §3.3.1/4:

Given a set of declarations in a single declarative region, each of which specifies the same unqualified name,

  • they shall all refer to the same entity, or all refer to functions and function templates; or

  • exactly one declaration shall declare a class name or enumeration name that is not a typedef name and the other declarations shall all refer to the same variable or enumerator, or all refer to functions and function templates; in this case the class name or enumeration name is hidden (3.3.10). [Note: A namespace name or a class template name must be unique in its declarative region (7.3.2, Clause 14). —end note]

However, the wording here only says what is not directly invalid. A declaration can be disallowed by other rules even if it's not disallowed by this one. For example, there is such a restriction for class member declarations:

C++14 §9.2/1:

[…] A member shall not be declared twice in the member- specification, except that a nested class or member class template can be declared and then later defined, and except that an enumeration can be introduced with an opaque-enum-declaration and later redeclared with an enum-specifier.

I fail to find such a restriction that supports the apparently incorrect comment in C++14 §7.3.3/10 quoted at the start above, i.e. I fail to find any special treatment of block scopes or namespace scopes, and so a tentative conclusion (keeping in mind the comment's survival in spite of being contested already in 1998) is that the contested comment actually is wrong and that this question's code, where two declarations in the same declarative region refer to the same entity, is valid and should be accepted by all compilers.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • Diagnostics required? A bug in MSVS and Clang? – Supremum Jul 04 '15 at 20:53
  • @Supremum: The paragraph doesn't say “no diagnostics required”, so it's a diagnosable rule according to §1.4/1, and according to §1.4/2, “If a program contains a violation of any diagnosable rule or an occurrence of a construct described in this Standard as “conditionally-supported” when the implementation does not support that construct, a conforming implementation shall issue at least one diagnostic message.” – Cheers and hth. - Alf Jul 04 '15 at 20:58
  • Then we have a bug in MSVS and Clang. – Supremum Jul 04 '15 at 20:59
  • @Supremum: Here's a [link to the relevant parts of Microsoft Connect](https://connect.microsoft.com/VisualStudio/feedback/details/1488317/-pragma-once-fails-it-includes-the-same-file-twice-via-2-different-paths) for reporting such bug. It's the latest bug I reported there. As you can see there have been some changes recently that make it difficult to e.g. describe steps to reproduce, but I just used comments. – Cheers and hth. - Alf Jul 04 '15 at 21:03
  • 2
    Thanks, I'll report the bug to MSVS and Clang. – Supremum Jul 04 '15 at 21:12
  • I tried to report the bug to Clang first: https://llvm.org/bugs/show_bug.cgi?id=24029 They want to know why multiple declaration are not allowed in block scope. Do you have a reference from the standard for that? – Supremum Jul 05 '15 at 11:15
  • @Supremum: The only ref is the non-normative comment I quoted from C++14, which has been there since (including) C++98 But apparently it's **wrong**, and still survived through TC1 (C++03), C++11 and C++14. Richard Smith in his answer cites [core issue 36](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#36) about it. The discussion of multiple declarations of the same name is in C++14 §3.3.1/4. It's also verbatim like the original C++98 text, AFAICS. – Cheers and hth. - Alf Jul 05 '15 at 21:47
  • Yeah, I saw Richard Smith answer. I should stop reading everything that is non-normative in the standard since it seems to be extremly long-lived after errors have been found. – Supremum Jul 06 '15 at 01:15
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/82437/discussion-between-supremum-and-cheers-and-hth-alf). – Supremum Jul 06 '15 at 03:35
6

Clang and MSVC are correct; this code is valid. As Alf notes, [namespace.udecl] (7.3.3)/10 says

A using-declaration is a declaration and can therefore be used repeatedly where (and only where) multiple declarations are allowed.

However, there is no restriction on multiple declarations of the same entity in block scope, so the original example is valid. A corresponding case not involving using-declarations is:

int n;
void f() {
  extern int n;
  extern int n;
}

This is valid (and is accepted by GCC, EDG, Clang, and MSVC), therefore (by the above-quoted rule) the original example is also valid.

It is worth noting that the example in [namespace.udecl] (7.3.3)/10 contains an error. It says:

namespace A {
  int i;
}

void f() {
  using A::i;
  using A::i; // error: double declaration
}

... but the comment is not correct; there is no error on the second declaration. See the discussion in core issue 36. I've removed the example from the standard so that it won't confuse more people.

Richard Smith
  • 13,696
  • 56
  • 78
  • Yes, looks like the example in the standard is incorrect. Why didn't they just remove that a long time ago? – Supremum Jul 05 '15 at 23:21
  • @Supremum I agree, so I [removed the example](https://github.com/cplusplus/draft/commit/299ec33999ca8d57ce40d5663373d8da46de0baa). – Richard Smith Jul 06 '15 at 00:59
  • Awesome! If I understand core issue 36 correctly, it's still open because of an ambiguity that has not been resolved yet (agreement was not made). I guess this ambiguity implies that GCC doesn't have a bug either here, it just choses an other interpretation than clang? That should explain the difference in behaviour. – Supremum Jul 06 '15 at 01:24
  • Could you please also have a look at the OP's other two related questions? ([This one](http://stackoverflow.com/q/31220154), [that one](http://stackoverflow.com/q/31230903)) – Kerrek SB Jul 06 '15 at 06:31