Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using declaration compiles in gcc and msvc but rejected in clang

I have written the following program to clear my using declaration concept. But surprisingly, the code compiles with gcc and msvc but rejected by clang.

namespace X 
{ 
    struct type {}; 
}
namespace Y 
{ 
    using type = int; 
}
using X::type;
using Y::type;
int main()
{
    auto i = sizeof(type); //gcc and msvc accepts this but clang rejects this
}

Check Demo

As you can see the above program compiles with gcc and msvc but clang says:

<source>:13:21: error: reference to 'type' is ambiguous
    auto i = sizeof(type); 
                    ^
<source>:10:10: note: candidate found by name lookup is 'type'
using Y::type;
         ^
<source>:9:10: note: candidate found by name lookup is 'type'
using X::type;
         ^
<source>:13:21: error: reference to 'type' is ambiguous
    auto i = sizeof(type); 
                    ^
<source>:10:10: note: candidate found by name lookup is 'type'
using Y::type;
         ^
<source>:9:10: note: candidate found by name lookup is 'type'
using X::type;
         ^

My question is what is the correct behavior according to the standard. Which is the right compiler here.

like image 829
Vlad from Moscow Avatar asked Feb 02 '26 21:02

Vlad from Moscow


2 Answers

Pre C++23

The program is ill-formed(see gcc bug) because a using-declaration is a declaration and so it must follow the rule of a declaration. In particular, the two given using-declarations declares a type named type(in the global namespace) that refers to two different types X::type and Y::type=int .

Next, there cannot be more than one type declaration(in the same scope) with the same name that refers to two different types. This is explained in more detail below using the c++ standard:

Also from using declaration's documentation:

Since a using-declaration is a declaration, the restrictions on declarations of the same name in the same declarative region also apply to using-declarations.

(emphasis mine)

Next, basic.scope.declarative prevents declarations of same name that refers to two different types(which is the case in your example):

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, non-static data member, or enumerator, or all refer to functions and function templates; in this case the class name or enumeration name is hidden ([basic.scope.hiding]).

(emphasis mine)

And since type refers to 2 different entities, the program is ill-formed.


Testing the above conclusion

Also note that besides the above quoted reference you can also test the above conclusion by replacing using type = int; with struct type{}; in namespace Y and you'll notice that we get the expected error. demo:

namespace X 
{ 
     struct type{};
}
namespace Y 
{ 
    struct type{};  
}
using X::type;
using Y::type;      //NOT ALLOWED since same name "type" refers to different structs

Furthermore, if you change the above program to define type to be same type say as int then program will work since this time name refers to same type int. demo

namespace X 
{ 
     using type= int ; 
}
namespace Y 
{ 
    using type= int;  
}
using X::type;
using Y::type;       //ALLOWED since "type" refer to same type int 

Here is the gcc bug:

GCC accepts invalid program with multiple using declarations


C++23

The program is also ill-formed in C++23 but now due to the statement auto i = sizeof(type); instead of using Y::type;.

From C++23's [namespace.udecl]:

If a declaration named by a using-declaration that inhabits the target scope of another declaration potentially conflicts with it (6.4.1), and either is reachable from the other, the program is ill-formed. If two declarations named by using-declarations that inhabit the same scope potentially conflict, either is reachable from the other, and they do not both declare functions or function templates, the program is ill-formed.

[Note 4 : Overload resolution possibly cannot distinguish between conflicting function declarations. — end note]

(emphasis mine)

like image 78
Anoop Rana Avatar answered Feb 05 '26 11:02

Anoop Rana


It seems the both compilers, gcc and msvc, have a bug.

The name type is redeclared in the global namespace with different meanings.

From the C++20 Standard (9.9 The using declaration)

12 Since a using-declaration is a declaration, the restrictions on declarations of the same name in the same declarative region (6.4) also apply to using-declarations.

and (6.4.1 Declarative regions and scopes)

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

(4.2) — 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, non-static data member, or enumerator, or all refer to functions and function templates; in this case the class name or enumeration name is hidden (6.4.10).

like image 27
Vlad from Moscow Avatar answered Feb 05 '26 12:02

Vlad from Moscow



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!