Can namespace functions be declared at block scope? - c++

Can namespace functions be declared at block scope outside the namespace they were defined at?
This code does not compile when DECLARED_IN_NS is defined as 1:
#define DECLARED_IN_NS 1 // can be either 0 or 1
#if DECLARED_IN_NS == 1
namespace ns
{
#endif
void
func1()
{
}
void
func2()
{
}
#if DECLARED_IN_NS == 1
} // namepace ns
#endif
int main( )
{
#if DECLARED_IN_NS == 1
void ns::func1(); // compile error
void ns::func2(); // compile error
ns::func1();
ns::func2();
#elif DECLARED_IN_NS == 0
void func1();
void func2();
func1();
func2();
#endif
}
It shows some errors:
error: qualified-id in declaration before '(' token
28 | void ns::func1();
| ^
The code compiles when func1 and func2 are defined at global namespace. However, it doesn't compile when they are defined inside a namespace (e.g. ns).
Is there a way to fix this?

I can't find a definitive reference to this in the standard, but I don't think there'd be much point to allow it intentionally.
Even if it could appear at block scope, it'd be pointless. A declaration that uses a nested name specifier cannot be the first declaration for an entity.
[dcl.meaning.general]
1 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 ([namespace.def])) ...
This makes this code like this valid
namespace ns
{
void func();
} // namepace ns
void ns::func(); // repeated declaration - optional
void ns::func() { // definition
}
Now, returning to the block scope case, you'd need a namespace scoped declaration preceding it anyway, so the whole exercise is moot.
Furthermore, the behavior of declaring functions in block scope has aspects that are aptly named by the c++ community, but it remains due to C compatibility. It would certainly not be beneficial to allow it for nested names intentionally.

Related

Undefined symbol error when accessing an extern bool

I have driver.h which contains:
namespace org::lib{
extern bool masterBool;
}
And a library.h which contains an anonymous namespace that defines masterBool:
namespace {
bool masterBool = false;
std::string otherFunction(){
//....
}
}
My driver.cpp which calls the otherFunction() and masterBool:
#include driver.h
#include library.h
namespace org::lib{
void function(){
otherFunction();
if (masterBool){
//.....
}
}
}
Upon compiling, I'm getting a undefined symbol: org::lib::masterBool in my driver.cpp. However, otherFunction() call did not such an error, even though they are both defined in the anonymous namespace of library.h
What am I missing here?
::org::lib::masterBool is not the same variable as ::(unnamed)::masterBool and is never defined. (They wouldn't be the same even if the unnamed namespace appeared inside org::lib.) There aren't two otherFunctions (in any one translation unit—see below), so that problem can't occur there.
More generally, don't use unnamed namespaces in header files; even the rare recommendations are better served by inline variables (or some emulation of them prior to C++17).

Static variables and different namespaces in c++

I have read on static variables and know what they are, how they function when define in a .cpp file, function, and class.
But I have slight confusion when define in different namespaces in the same cpp file.
One of these voted answers here says:
When you declare a variable as static inside a .h file (within or without namespace; doesn't matter), and include that header file in various .cpp files, the static variable becomes locally scoped to each of the .cpp files.
According to the program below, namespaces does matter.
static int a = 0;
namespace hello1 {
static int a = 1;
namespace hello2 { static int a = 2;}
namespace hello3 { static int a = 3;}
}
int main()
{
std::cout<<::a<<hello1::a<<hello1::hello2::a<<hello1::hello3::a;
return 0;
}
output
0123
Here is what I think: a outside namespaces is the file scope. a in hello1 is hello1 scope. Since hello1 is global scope namespace, I can only have one a in namespace hello1, including other cpp files where hello1 exists. The same goes for hello2 and hello3; only one a in each hello1::hello2 and hello1::hello3.
Please correct me If I understood it correct. The namespace does matter; static variables in the same cpp file under different namespaces are different entities.
That's not what the passage you quoted is talking about. It is referring to the fact that static variable names have internal-linkage. What that means is that you can't access a static variable a that is defined in "a.cpp" from "b.cpp", even if you include a declaration of a in "b.cpp". That declaration will refer to a different object.
For example:
A.hpp
static int a = 0;
void foo();
A.cpp
#include "A.hpp"
#include <iostream>
void foo() { std::cout << a << '\n'; }
B.cpp
#include "A.hpp"
#include <iostream>
int main() {
std::cout << a << '\n'; // prints 0
a = 1;
foo(); // prints 0, since it uses a different a
}
In this example, "A.cpp" and "B.cpp" see different global-scope objects named a since a was declared static. Even though main modifies a's value to be 1, foo still sees it as 0 since it's looking at a different a.
This behavior is irrespective of namespaces. If a were in a namespace other than the global namespace this same behavior would occur. That is what the passage you quoted is referring to.

Extending a namespace in an inline namespace that initially was defined in the enclosing namespace of the inline namespace

I would like to check myself whether I understand correctly the following quote below from the C++ 20 Standard (English is not my native language).
Section 9.7.1 Namespace definition:
2 In a named-namespace-definition, the identifier is the name of the
namespace. If the identifier, when looked up (6.4.1), refers to a
namespace-name (but not a namespace-alias) that was introduced in the
namespace in which the named-namespace-definition appears or that was
introduced in a member of the inline namespace set of that namespace,
the namespace-definition extends the previously-declared namespace.
Otherwise, the identifier is introduced as a namespace-name into the
declarative region in which the named-namespacedefinition appears.
That is may a namespace be defined in a namespace and then extended in one of its inline namespace? Or vice versa. May a namespace be defined in an inline namespace and then be extended in its enclosing namespace?
Here is a demonstrative program.
#include <iostream>
inline namespace N1
{
inline namespace N2
{
namespace N3
{
void f( int ) { std::cout << "f( int )\n"; }
}
}
namespace N3
{
void f( char ) { std::cout << "f( char )\n"; }
}
}
int main()
{
N3::f( 10 );
N3::f( 'A' );
}
The program output is
f( int )
f( char )
However for this program the compiler issues an error saying that reference to 'N3' is ambiguous.
#include <iostream>
inline namespace N1
{
namespace N3
{
void f( int ) { std::cout << "f( int )\n"; }
}
inline namespace N2
{
namespace N3
{
void f( char ) { std::cout << "f( char )\n"; }
}
}
}
int main()
{
N3::f( 10 );
N3::f( 'A' );
}
may a namespace be defined in a namespace and then extended in one of its inline namespace?
No. In your second example, when the compiler sees the definition of N3 inside N2, the lookup of N3 finds the N3 that was declared outside N2, but that N3 was not "introduced in the namespace in which the named-namespace-definition appears" (since it's not inside N2), or "introduced in a member of the inline namespace set of that namespace" since the inline namespace set of N2 is the set of transitively inline namespaces inside N2. Thus, this new definition of N3 doesn't extend the previous one.
May a namespace be defined in an inline namespace and then be extended in its enclosing namespace?
Yes. In your first example, N3 when looked up finds N2::N3 which was "introduced in a member of the inline namespace set" since N2 is part of the inline namespace set of N1.
We can understand the rationale for this apparent asymmetry as follows: in the second example, when N3 is initially defined, it's being defined as a member of N1 and not a member of any inline namespace of N1. Later, when N2::N3 is being defined, if it were to extend N1::N3, it would be retroactively making N3 a member of N2, which goes against common sense and is therefore disallowed. In the first example, N3 is initially defined as a member of the inline namespace N2 and there is no issue with extending it with the second definition later. (Though I cannot see an obvious reason why this is useful.)
I'll try to rewrite the standard wording in a less dense (but possibly less precise) way:
We are lexically inside namespace X.
We encounter something that looks like namespace Y { ... }.
We look up Y. If Y is not a namespace name, move on1.
Was Y introduced (directly) in X? If so, move to point 6.
Was Y introduced in a namespace that is part of the inline set2 of namespaces of X? If yes, move to 6. If not, move on1.
Y does extend the namespace we found in step 3.
1 "Move on" in the sense of "ignore the cited standard paragraph".
2 The inline namespace set of N is the transitive closure of all inline namespaces in N.
may a namespace be defined in a namespace and then extended in one of its inline namespace?
No. We only consider the directly enclosing namespace (N3 is extended in N1 in your first example, and in N2 in your second one) and its inline namespaces. This is why your second example does not work: N2 (or its inline set of namespaces) does not contain any previous definition of N3.
May a namespace be defined in an inline namespace and then be extended in its enclosing namespace?
Yes.
The examples you show are consistent with the standard wording.

Difference between defining globally and within scope

So, I have a variable declared globally. For it to be used, it has to be defined. I get different results based on whether I define it globally or within scope of the main function.
Here's the basic code:
// main.cpp
//int variable = 3;
int main()
{
//int variable = 5;
func();
return variable;
}
// source.cpp
#include "source.hpp"
void func()
{
cout << variable << endl;
}
// source.hpp
#ifndef __SOURCE_HPP_INCLUDED__
#define __SOURCE_HPP_INCLUDED__
#include<iostream>
using namespace std;
extern int variable;
void func();
#endif // __SOURCE_HPP_INCLUDED__
So, if I define globally (outside main), then everything works. But if I define within main, then I get "undefined reference to 'variable'" errors. But I only call upon source.cpp when I'm inside main; so why do I get this error, if variable is defined within the same "scope" as func? Is it the compiler just preemptively giving the error before linking is done? Or is it related to the fact that I declared it as "extern"?
You can't define a global variable inside a function. You're actually defining a different, unrelated local variable that just happens to have the same name.
func references the global variable, so you must have a definition for it.

Do inline namespace variables have internal linkage? If not, why does the code below work?

This question is directly related to this one. Consider the code:
#include <iostream>
inline namespace N1
{
int x = 42;
}
int x = 10;
int main()
{
extern int x;
std::cout << x; // displays 10
}
It displays 10. If I remove the extern int x; declaration then we get an ambiguity compiler time error
error: reference to 'x' is ambiguous
Question: why does the code work with the extern int x declaration work, and why does it stop working when I remove it? Is it because inline namespace variables have internal linkage?
No. There is no provision in [basic.link] that would cause x to have internal linkage. Specifically, "All other namespaces have external linkage.", and "other" refers to "not unnamed". Perhaps you were thinking of unnamed namespaces?
No, the code works because to avoid breaking existing C code, extern int x; has to work the same way in did in C, in other words creating a local extern to a global namespace (that's all we had in C) variable. Then when you use it later the locally declared extern removes any possible ambiguity.