Ambiguous reference to namespace within an inline namespace - c++

Assume the following code:
namespace test
{
namespace detail
{
}
inline namespace v1
{
namespace detail
{
void foo()
{
}
}
}
}
int main()
{
test::detail::foo();
}
As we can see, this code compiles with Clang; not with GCC, however - GCC complains that the reference to namespace detail is ambiguous:
main.cpp:20:11: error: reference to 'detail' is ambiguous
test::detail::foo();
^
main.cpp:4:5: note: candidates are: namespace test::detail { }
{
^
main.cpp:10:9: note: namespace test::v1::detail { }
{
^
Which compiler does the correct thing here?

GCC is correct:
Members of an inline namespace can be used in most respects as though they were members of the enclosing namespace. Specifically, the inline namespace and its enclosing namespace are both added to the set of associated namespaces used in argument-dependent lookup (3.4.2) whenever one of them is, and a using-directive that names the namespace is implicitly inserted into the enclosing namespace as for an unnamed namespace (7.3.1.1). Furthermore, each member of the inline namespace can subsequently be explicitly instantiated (14.7.2) or explicitly specialized (14.7.3) as though it were a member of the enclosing namespace. Finally, looking up a name in the enclosing namespace via explicit qualification (3.4.3.2) will include members of the inline namespace brought in by the using-directive even if there are declarations of that name in the enclosing namespace.
(This is at 7.3.1/8 in old n3337 numbering)
I believe you're seeing Clang bug #10361.

GCC is correct.
N3797 states that,
and a using- directive (
7.3.4 ) that names the inline namespace is implicitly inserted into the enclosing namespace as for an unnamed namespace (
7.3.1.1 ).
Thus, test::detail is not the same namespace as test::v1::detail, so the lookup of test::detail is ambiguous. The Standard is exceptionally clear that the lookup of test::detail should include test::v1::detail, there are many quotes in this section to support this, but nothing to state that they should be considered the same namespace.
Arguably, I would say that Clang's behaviour is superior, but GCC's is correct.

Related

variables declaration in anonymous namespace and definition in other place

Can someone explain why I can't define variable that was declared in anonymous namespace as global variable in another place?
#include <iostream>
namespace {
extern int number;
}
int number = 123;
void g() {
std::cout << number;
}
Compiler says that "Reference to 'number' is ambiguous" but I can't understand why it recognises declaration and definition as different things? Thank you in advance.
In general, the name being declared by a declaration is not looked up—after all, you can’t rely on finding a previous declaration when a name is first introduced. Of course, there is a similar process that traps things like
int x;
float x;
However, since it isn’t lookup it is not affected by using at all (including for an unnamed namespace). Another way of describing this distinction is that a declaration puts entities into namespaces and thus need not consider any other namespace in order to decide where to put an entity.
There are also cases where lookup does occur for (what might be) a declarator-id:
namespace N {using X=int;}
// using namespace N;
struct A {
A(X()); // ?
};
A has a member function with no parameters returning an A named X (with meaningless parentheses around its declarator); however, with the using-directive it instead has a constructor that takes a pointer to a function of no parameters returning an int. Similarly, in a declaration beginning
template<>
struct X<…
X must be fully looked up, even though a declaration of an explicit specialization must inhabit the same scope as the primary template (with leeway for inline namespaces), because it might continue
template<>
struct X<int>::Y<char> {…};
and not be a specialization of X at all.
For the unqualified name-lookup the compiler considers also nested unnamed namespaces in the global namespace,
You declared two different objects with the same name in the global namespace and in the nested unnamed namespace.
The using directive for unnamed namespace is implicitly inserted in the enclosing namespace.
Consider the following demonstration program
#include <iostream>
namespace N
{
extern int number;
}
using namespace N;
int number = 123;
int main()
{
std::cout << number << '\n';
}
The compiler will issue an error due to the ambiguity for the unqualified reference to the name number in this statement
std::cout << number << '\n';
The similar situation takes place with an unnamed namespace because the using directive is implicitly inserted in the enclosing namespace.
From the C++ 20 Standard (9.8.2 Namespace definition)
7 Members of an inline namespace can be used in most respects as
though they were members of the enclosing namespace. Specifically, the
inline namespace and its enclosing namespace are both added to the set
of associated namespaces used in argument-dependent lookup (6.5.3)
whenever one of them is, and a using directive (9.8.4) that names
the inline namespace is implicitly inserted into the enclosing
namespace as for an unnamed namespace (9.8.2.2).

Why does the using directive not "associate" with ordinary functions?

According to this question it is valid to define class methods after a using directive, instead of enclosing them within a namespace block.
However, that seems not to be the case for ordinary functions. Consider:
Greeting.hh
#pragma once
namespace NS
{
class Greeting
{
public:
void hello();
};
void otherHello();
}
Greeting.cc
#include "Greeting.hh"
#include <iostream>
using namespace NS;
void Greeting::hello()
{
std::cout << "Greeting::hello" << std::endl;
}
void otherHello()
{
std::cout << "otherHello" << std::endl;
}
main.cc
#include "Greeting.hh"
int main()
{
NS::Greeting o;
o.hello();
NS::otherHello();
}
This won't compile, yielding the following error message:
undefined reference to `NS::otherHello()'
Further inspection indicates that otherHello's symbol is not preceded by the namespace, while Greeting::hello's is:
g++ -std=c++14 -pedantic -Wall -c Greeting.cc
nm -C Greeting.o | grep T
000000000000002a T otherHello()
0000000000000000 T NS::Greeting::hello()
Does this contradict with the Standard reference from the accepted answer?
"During unqualified name lookup (3.4.1), the names appear as if they
were declared in the nearest enclosing namespace which contains both
the using-directive and the nominated namespace."
What's important to remember is that
Function declarations in different namespaces don't interfere with each other.
A definition of a function is also a declaration.
[namespace.def/4]
The enclosing namespaces of a declaration are those namespaces in
which the declaration lexically appears, except for a redeclaration of
a namespace member outside its original namespace (e.g., a definition
as specified in [namespace.memdef]). Such a redeclaration has the same
enclosing namespaces as the original declaration.
So let's look at the otherHello definition. Where does it lexically appear? In the global namespace of course. That's also the point of declaration for it. Which means the enclosing namespace is the global one, and you end up with a declaration of ::otherHello.
So no, this doesn't contradict the standard quote from the accepted answer to the other question. Member functions can be defined outside of the class, so long as they are qualified by their class name ([class.mfct/4]):
If the definition of a member function is lexically outside its class
definition, the member function name shall be qualified by its class
name using the ​::​ operator.
So we need only ask, does Greeting name the same class as NS::Greeting? Why, yes it does. The using directive is responsible for that.
I'll add this segment in the hope of clarifying. Consider this code snippet:
namespace NS1 {
namespace NS2 {
void hello();
}
}
using namespace NS1;
void NS2::hello() {
}
int main() {
NS1::NS2::hello();
return 0;
}
When the compiler encounters NS2::hello being defined it preforms name lookup for that declarator id. According to [basic.lookup.qual/3]:
In a declaration in which the declarator-id is a qualified-id, names
used before the qualified-id being declared are looked up in the
defining namespace scope; names following the qualified-id are looked
up in the scope of the member's class or namespace.
So NS2 is looked up in the defining scope (the global one), and according to the unqualified name lookup rule you quoted, it is found and resolved as NS1::NS2. That's how NS2::hello is associated with NS1::NS2::hello and resolved as defining it.
In the global namespace of the OP, otherHello isn't preceded by anything. Therefore name lookup doesn't occur. It immediately defines that function in the enclosing namespace, as I previously quoted.

Trying to define namespace member via using-declaration

Consider the following program. Is it well-formed or not according to the c++ standard (references to relevant parts of the standard needed):
namespace X { extern int i; }
namespace N { using X::i; }
int N::i = 1;
int main() {}
I'm getting different results for different compilers. I'm trying to figure out for what compiler I should file a bug report for:
Clang: Gives the following compiler error: No member named 'i' in namespace 'N'
GCC and Visual C++ compiles it without errors.
For comparison the following gives compiler error with all three compilers:
namespace X { void f(); }
namespace N { using X::f; }
void N::f() {};
int main() {}
Current working draft N4527, [8.3p1]:
[...] When the declarator-id is qualified, the declaration shall refer to a
previously declared member of the class or namespace to which the
qualifier refers (or, in the case of a namespace, of an element of the
inline namespace set of that namespace (7.3.1)) or to a specialization
thereof; the member shall not merely have been introduced by a
using-declaration in the scope of the class or namespace nominated by
the nested-name-specifier of the declarator-id. [...]
So, definitely ill-formed; GCC and MSVC are wrong.

Unqualified name lookup finds inline namespace member

I've written the following code:
#include <iostream>
inline namespace M
{
int j=42;
}
int main(){ std::cout << j << "\n"; } //j is unqualified name here.
//Hence, unqualified name lookup rules will be applied.
//This implies that member of inline namespace shall not be considered.
//But it is not true
And it works fine. But I'm expected that the that program is ill-formed. It is because the Standard said (N3797, sec. 7.3.1/7):
Finally, looking up a name in the enclosing namespace via explicit
qualification (3.4.3.2) will include members of the inline namespace
brought in by the using-directive even if there are declarations of
that name in the enclosing namespace.
Also the section 3.4.1/6 does not said anything about involving of inline namespace in the unqualified name lookup:
A name used in the definition of a function following the function’s
declarator-id 28 that is a member of namespace N (where, only for the
purpose of exposition, N could represent the global scope) shall be
declared before its use in the block in which it is used or in one of
its enclosing blocks (6.3) or, shall be declared before its use in
namespace N or, if N is a nested namespace, shall be declared before
its use in one of N’s enclosing namespaces.
It is a g++ bug or I understood that rules incorrectly?
There's no bug..
No, it's not a bug in neither g++ (or clang++) which has the behavior described, the compiler is supposed to find j.
inline namespace N {
int j;
}
int main () {
int a = j; // legal, `j` == `N::j`
}
What does the Standard say?
You are missing a very important section of the standard, namely 7.3.1§8, where it states that the enclosing namespace of an inline namespace implicitly has a using directive that refers to the inline namespace.
[7.3.1]p8 namespace definition [namespace.def]
Members of an inline namespace can be used in most respects as thought they were members of the enclosing namespace. Specifically, the inline namespace and its enclosing namespace are both added to the set of associated namespaces used in argument-dependent lookup (3.4.2) whenever one of them is, and a using-directive (7.3.4) that names the inline namespace is implicitly inserted into the enclosing namespace as for an unnamed namespace (7.3.1.1).
Elaboration
This means that our previous example is semantically equivalent to the below, where we have introduced a using-directive to bring the names from our nested namespace into the global namespace:
inline namespace N {
int j;
}
using namespace N; // the implicit using-directive
int main () {
int a = j; // legal
}

Redefinition member of the namespace into the nested inline namespace

There is a quote from 7.3.1/8 of N3797:
Members of an inline namespace can be used in most respects as though
they were members of the enclosing namespace.
Consider the following code snippet:
namespace M
{
int j = 7;
inline namespace MM
{
int j = 8;
}
}
I think that the example violates the ODR. But it is not true and it is compiling successful. Can you explain that behavior?
Introduction
7.3p1 Namespaces [basic.namespace]
A namespace is an optionally-named declarative region. The name of a namespace can be used to access entities declared in that namespace; that is, the members of the namespace. Unlike other declarative regions, the definition of a namespace can be split over several parts of one or more translation units.
A declared entity inside a namespace belongs to that namespace, ie. it's a member of that specific namespace, no matter if the namespace is inline or not.
ODR VIOLATION = N0NE
Your example snippet does not violate the ODR, mainly because you have 2 different entities named j;
namespace N {
int j = 0; // 1st
inline namespace M {
int j = 1; // 2nd
}
}
As pointed out further down in [namespace.def]p8, name lookup in the enclosing namespace will include those found in any inline namespace, but the members of the nested inline namespace are still entities of their own.
7.3.1p8 Namespace definition [namespace.def]
Specifically, the inline namespace and its enclosing namespace are both added to the set of associated namespaces used in argument-dependent lokoup (3.4.2) whenever one of them is, and a using-direction (7.3.4) that names the inline namespace is implicitly inserted into the enclosing namespace as for an unnamed namespace (7.3.1.1).
Furthermore, each member of the inline namespace can subsequently be explicitly instantiated (14.7.2) or explicitly specialized (14.7.3) as though it were a member of the enclosing namespace. Finally, lookup up a name in the enclosing namespace via explicit qualification (3.4.3.2) will include members of the inline namespace brought in by the using-directive even if there are declarations of the name in the enclosing namespace.
The added names are not treated as redeclarations of previously declared entities, they are additional names, in a nested declarative region, that are brought into the enclosing namespace during name-lookup.
Note: Relying on the compiler to issue a diagnostic in terms of ODR-violations is not safe, mainly because the Standard explicitly states that "no diagnostic [is] required" if an application violates the rules set up by [basic.def.odr].Further details are avaiable in a comment by Matthieu M. on this post.