I was testing the name lookup rules for C++. I've got a simple program having 3 files:
$cat testns01.h
struct S{
static int f(){return 1;}
};
$cat testns02.h
namespace S{
static int f(){return 2;}
}
$cat testns3.cpp
#include "testns02.h" // namespace
#include "testns01.h" // struct
#include<stdio.h>
int main()
{
int i = S::f();
printf("%d\n",i);
return 0;
}
If I compile and run, I get:
$g++ testns3.cpp && ./a.out
1
OK, I have 3 questions:
name "S" duplication between class name and namespace doesn't conflict?
When both has name "S", seems "struct S" has higher priority
If I comment the line of #include "testns01.h", the program will print 2, still OK. So my third question is:
Does the c++ standard talk about how name lookup resolves duplicated name duplication?
name "S" duplication between class name and namespace doesn't conflict?
They do.
When both has name "S", seems "struct S" has higher priority
It doesn't. (look below)
Does c++ stardard talke about how name lookup resolves duplicated name duplication?
Yes. Quoting the relevant part of N4140:
§3.3.1 [basic.scope.declarative] / 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. [ Note: A namespace name or a class template name
must be unique in its declarative region. —end note ]
I think you accidentally got your example working for you, because you repeated the include guards. I was able to reproduce the "preference" for class S from the question:
#ifndef FOO
#define FOO
struct S{
static int f(){return 1;}
};
#endif
#ifndef FOO
#define FOO
namespace S{
static int f(){return 2;}
}
#endif
#include<stdio.h>
int main()
{
int i = S::f();
printf("%d\n",i);
return 0;
}
link
Related
Just trying to grab the concept revolving around the scope of the Class in C++. If we take an example something like:
#include <iostream>
using namespace std;
string barValue="OUTSIDE CLASS";
class foo
{
public:
void print_bar()
{
cout<<barValue<<endl;
}
};
int main()
{
foo f;
f.print_bar();
return 0;
}
I am wondering:
Is the barValue variable is in the Class scope?
Can the barValue be accepted even if it is defined in one of the included files?
Is the file and all the included files can be called as the scope of the foo Class?
I can understand that the barValue will not be considered if it is defined after the Class body. To follow up is that means all the included files comes before the Class body and in general before the current file content?
Question 1
Is the barValue variable is in the class scope?
barValue is a global variable since you have defined it outside any function and class. Therefore barValue can be used inside the class as you did.
Question 2
Can the barValue be accepted even if it is defined in one of the included files?
Yes even if barValue is defined in a header file it can be used in other files because/if it has external linkage as shown in the below example.
file.h
int barvalue = 34;//barvalue has external linkage
main.cpp
#include <iostream>
#include "file.h"
int main()
{
std::cout<<barvalue<<std::endl;//prints 34
return 0;
}
Question 3
Is the file and all the included files can be called as the scope of the foo class?
No
Question 4
I can understand that the barValue will not be considered if it is defined after the class body. To follow up is that means all the included files comes before the Class body and in general before the current file content?
You should clarify this question more because i think it is not clear what your are asking here but i will try to answer what i understand from your question.
If you define barValue after the class foo definition then the program will not work because since you have defined it after the class definition, therefore it has not been defined/declared at the point when you write cout<<barValue<<endl; which is what the error says as shown below:
#include <iostream>
using namespace std;
class foo
{
public:
void print_bar()
{
cout<<barValue<<endl;//error: ‘barValue’ was not declared in this scope
}
};
int main()
{
foo f;
f.print_bar();
return 0;
}
string barValue="OUTSIDE CLASS";
Here is the code example:
namespace A
{
int k;
}
void k(int,int){/*dosomething*/}
int main()
{
using namespace A;
k(1,1);//ooop!k is ambiguous!
}
What happened? I thought it should not be ambiguous since they are different types. Why is it ambiguous? With int k it is not possible to do k(1,1).
So it has nothing to do with what the name actually is?Even if a name that is not a function type will also cause ambiguity when we use k(1,1) ,which is wrong in grammar because int k is not function?
Lookup of the name k is ambiguous because there are two matching declarations visible, ::k and ::A::k .
The exact rule can be found in the C++ Standard (N4659 [basic.lookup]/1):
Name lookup associates the use of a name with a set of declarations of that name. The declarations found by name lookup shall either all declare the same entity or shall all declare functions; in the latter case, the declarations are said to form a set of overloaded functions.
Looking up an unqualified name that is used for a function call has two stages:
Unqualified lookup of the name
Argument-dependent lookup of the name.
The unqualified name lookup rules, even when looking up a name that is being used for a function call, find any declaration of that name. (The rule is NOT that it only searches for function declarations of that name). This stage finds both ::k and ::A::k regardless of whether those are functions, ints, or whatever.
The argument-dependent lookup does have a rule that only function declarations are found for that part of the lookup. But that is not relevant to this code.
The relevant behaviour of the using directive is covered by [basic.lookup.unqual]/2 (edited by me to just show the parts relevant to this question):
For the purpose of the unqualified name lookup rules, the declarations from the namespace nominated by the using-directive are considered members of that enclosing namespace.
This clarifies that using namespace A; does not actually introduce the members of A into the scope of main(); but it means that when looking up a name in the global namespace (because that is the innermost enclosing namespace of the site of the using declaration), the names from A will also be found there.
There are three ways to resolve the ambiguity:
1st:
int main() {
A::k = 5;
::k( 1, 1 );
}
2nd:
int main() {
using namespace A;
A::k = 5;
::k(1, 1);
}
3rd:
namespace A {
int k;
}
namespace B {
void k( int, int ) { /* do something */ }
}
int main() {
using namespace A or B but not both!
if A then k = 5; okay && k(1,1); error
if B then k(1, 1); okay && k = 5; error
if both again ambiguous unless A::k = 5; || B::k(1,1);
return 0;
}
Due to the nature of ambiguity it doesn't truly pay to use using namespace A. And this is why it is considered bad practice to have using namespace std; either in the global scope or directly in the main function. It is okay to use it in a function or a member function of a class/struct as long as you don't conflict with any other library.
I ran this in my IDE visual studio 2017 CE and here is the compiler error:
1>------ Build started: Project: ChemLab, Configuration: Debug Win32 ------
1>main.cpp
1>c:\...\visual studio 2017\projects\chemlab\chemlab\main.cpp(17): error C2872: 'k': ambiguous symbol
1>c:\...\visual studio 2017\projects\chemlab\chemlab\main.cpp(8): note: could be 'void k(int,int)'
1>c:\...\visual studio 2017\projects\chemlab\chemlab\main.cpp(6): note: or 'int A::k'
1>Done building project "ChemLab.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
When you use the using namespace directive it will take everything that is in that namespace and make it visible to main. So now in main you have both the namespace A and the global namespace visible within main. Since you have both visible you now have 2 identifiers or symbols in the look up table named k. And when you call k(1, 1) it doesn't know which one you intended to choose.
This is no different than doing this:
main.cpp
#include <string>
class string {
public:
char* _chars;
};
int main() {
using namespace std;
string myString; // error ambiguous did you mean ::string or std::string?
return 0;
}
This might provide you with some more insight:
When the using directive is being used, don't think of the variable k and the function k as being declared in the same scope. They were previously declared in their own scope. The variable k is in ::A::k and the function void k(int,int){} is in ::k(int,int){}. In the main function when you apply the using namespace A; what happens here it takes every symbol after A:: and it kind of shifts it as if it were in the global :: scope for visibility. Now the compiler has to make a choice on the available symbols and sees I have a k and a k. Did you mean int k(){} or void k(int,int){}...
The ambiguity comes from the name. There is no way to overload a variable like you would a function/method, so there is a "clash" in the names.
In order to get the "k" that you want, you need to specify the namespace.
namespace A
{
int k;
}
void k(int, int) {/*dosomething*/ }
int main()
{
using namespace A;
::k(1, 1); // uses the global namespace
A::k = 5; // uses the namespace A
}
Or, to take the namespace out of the equation:
void k(int, int) {/*dosomething*/ }
void k(int, int, float) {}
int main()
{
int k;
// all of these are now ambiguous
k(1, 1);
k(1, 2, 0.4);
k = 5;
}
Why do some of these compile and some not compile?
Scenario 1: compilation error 'main' : redefinition; previous definition was 'data variable'
#include <iostream>
using namespace std;
int main;
int main(){ }
Scenario 2: compilation error syntax error : missing ';' before identifier 'obj
#include <iostream>
using namespace std;
class main { };
int main(){
main obj;
}
Scenario 3: working fine
#include <iostream>
using namespace std;
class main { };
int main(){
class main obj;
}
Scenario 4 : working fine
#include <iostream>
using namespace std;
class main {};
main obj;
int main(){ }
Name lookup works in terms of names. The name is looked up, independent of what that name designates.
It's not permitted for the same name to be declared as both a function and a variable, in the same scope.
However, it is permitted for class X to exist even if X is the name of a function or a variable. When you have class X, then the current scope gets X as a name (designating that class). If the same scope has X declared as both a typename and a variable name, then just looking up x finds the variable name.
If a new scope declares X then any use of X in that scope finds the name declared in its scope, this is called shadowing. (Name lookup searches the current scope, then the scope which enclosed that, and so on up to the global scope).
Error, attempts to declare main as both a function and a variable
To process main obj;, the name main is looked up. The current scope is checked first, and it finds the function name main; so it does not check the global scope. We say that the inner scope's name shadows the outer scope's name. So, it is the same error as you would get from void foo(); foo obj;
class main unambiguously says to look up the class name main; that does not find int main() so it falls back to checking the global scope.
nothing new, main obj; makes no difference .
Another interesting example would be:
int x;
class x { };
int main()
{
x = 1; // ok
x y; // error, `x` means the int
class x y; // ok
}
I'm learning C++ now. What are the complete legal entities that can be put in a namespace?
Legal entities here means valid members of a namespace
Oh, this is a real question. I'm coming from .net and I have the .net mindset.
Any code can be put inside namespace.
However main() function must be at global namespace. It cannot be put inside user-defined namespace.
namespace userns
{
int main()
{
return 0;
}
}
This program wouldn't compile link : http://www.ideone.com/k6SPc
Its because userns::main() will not be considered entry-point of the program; it became just like any other user function, not the standard main(). To compile it successfull, you've to add main() at global namespace:
namespace userns
{
int main()
{
return 0;
}
}
int main()
{
return 0;
}
This will compile link now : http://www.ideone.com/76Ynu
Anything can be put in a namespace (which is legal for C++, of course).
Actually, everything is in some namespace - the global namespace, if not specified.
Everything can be put in namespace except few "entities", which will not compile.
(1) Globally overloaded operator new and operator delete
namespace N
{
void* operator new (size_t size) // error
{ ... }
}
(2) Definition of the constructs which are declared in outer scope of the namespace; for example you have a class A declared globally then you cannot define its method inside your namespace N. In the same way, if you have method declared in a namespace N then you cannot put its definition inside namespace N::Nested (i.e. Nested is a namespace inside N).
//file
struct A {
void foo ();
static int i;
};
namespace N
{
int A::i = 0; // error
void A::foo() // error
{}
}
Demo: this is not allowed.
I remember at least these 2 restrictions from my experience. Don't know about specs.
I expected to be able to use a namespace alias in a class declaration but get a compiler syntax error.
struct MyClass
{
namespace abc = a_big_namespace;
void fn() {
abc::test();
}
};
The only way I can get it to work is to put the alias in every function.
void fn() {
namespace abc = a_big_namespace;
abc::test();
}
Additionally I would like to be able to use the alias for function parameters. I haven't found a work-around for that.
void fn(abc::testType tt) {
abc::test(tt);
}
Is there a way to do what I want?
EDIT: my solution
I found that I didn't need unnamed namespace for my particular problem and can simply do this:
namespace myspace
{
namespace abc = a_big_namespace;
struct MyClass
{
void fn(abc::testType tt) {
abc::test(tt);
}
};
}
To switch to the other library, which is what my alias namespace refers to I just change the alias. This method even allows me to have the same class in a single file twice, each time refering to a different library.
Thanks for all your help.
Namespace alias in the class definition is illegal, as specified by the language specification.
Its allowed in only in namespace scope or function scope.
You can make alias at namespace scope. But that will be make permanent alias which can be used from other files as well. But the solution is simple : you can use unnamed namespace to prevent alias (and therefore all symbols from the big namespace) from being visible from other files. This is how it can be done:
//MyFile.cpp
namespace myspace
{
namespace //this is unnamed namespace
{
namespace abc = a_big_namespace;
}
struct MyClass
{
void fn()
{
abc::test(); //don't worry, this will work!
}
};
}
//OtherFile.cpp
myspace::abc::test(); //error - that means, prevention worked.
The alias is not visible from other files. When compiling OtherFile.cpp, GCC (4.5.0) says,
'myspace::abc' has not been declared
That proves the alias abc is visible only in MyFile.cpp. Thanks to unnamed namespace.
Demo : http://www.ideone.com/2zyNI (though it doesn't demonstrate OtherFile concept. I cannot have more than one file at ideone.com)
The scope of a namespace alias is a code block.
So you can put it in any code block.
BUT, you can't put it inside a class, because that will mean it's a member of the class.
A namespace alias can't be a member.
More about namespace aliases:
What is the scope of a namespace alias in C++?
Namespaces
It works if you declare the alias outside of the struct.
You can of course also put the alias outside the class:
namespace abc = a_big_namespace;
struct MyClass {
void fn()
{ abc::test(); }
};