Interesting Behavior of Compiler with Namespaces

Interesting behavior of compiler with namespaces

This works for the function call expression:

f(a);

because the namespace that X::A belongs to is included in the lookup for the function f due to argument dependent lookup(ADL), cppreference explains ADL as follows:

Argument-dependent lookup, also known as ADL, or Koenig lookup, is the
set of rules for looking up the unqualified function names in
function-call expressions, including implicit function calls to
overloaded operators. These function names are looked up in the
namespaces of their arguments in addition to the scopes and namespaces
considered by the usual unqualified name lookup.

Argument-dependent lookup makes it possible to use operators defined
in a different namespace

This is covered in the draft C++ standard section 3.4.2 Argument-dependent name lookup:

When the postfix-expression in a function call (5.2.2) is an unqualified-id, other namespaces not considered
during the usual unqualified lookup (3.4.1) may be searched,
and in those namespaces, namespace-scope
friend function or function template declarations (11.3) not otherwise visible may be found

and goes on to say:

For each argument type T in the function call, there is a set of zero or more associated namespaces and a
set of zero or more associated classes to be considered. The sets of namespaces and classes is determined
entirely by the types of the function arguments (and the namespace of any template template argument).

and includes the following bullet:

If T is a class type (including unions), its associated classes are: the class itself; the class of which it is a
member, if any; and its direct and indirect base classes. Its associated namespaces are the namespaces
of which its associated classes are members
.[...]

and further down provides a similar example to your problem:

namespace NS {
class T { };
void f(T);
void g(T, int);
}

NS::T parm;
void g(NS::T, float);

int main() {
f(parm); // OK: calls NS::f
extern void g(NS::T, float);
g(parm, 1); // OK: calls g(NS::T, float)
}

The function call expression:

g(5);

does not work because ADL does not add any namespaces for arguments that are fundamental types.

Herb Sutter covers ADL in Gotw #30 and in What's In a Class? - The Interface Principle.

Namespaces confusion

The using directive modifies name lookup in a way that isn't exactly intuitive to most programmers. The standard says this in [namespace.udir]p2:

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.

This wording means that names from the namespace do not appear in the current scope, but in some outer scope. In your example the using directive is in the function which is in the global namespace, and the Jill is also in the global namespace, so the names from Jill appears as if they are in the global namespace. (As Joachim said, the names aren't actually introduced there, so they don't immediately conflict with existing names and you only get ambiguous lookups when you actually use them.)

This is a simple case and the compiler gives you an error, which is good. It can actually get more complicated than that.

namespace Outer {
namespace Mid1 { int i = 1; }
namespace Mid2 {
namespace Tricky {
int i = 2;
namespace Inner {
void f() {
using namespace Mid1;
std::cout << i;
}
}
}
}
}

This will output 2, not 1, even though you had the using directive right next to the line referring to i. But the nearest enclosing namespace that contains both Mid1 and the using directive is Outer, so Mid1::i acts as if it was declared in Outer. If you had an Outer::i it would be shadowed by Tricky::i, and Mid1::i fares no better.

An easy solution is to ban using directives and only use using declarations and namespace aliases. They're far more intuitive.

odd behavior of c++ namespace

The feature that is confusing you is called ADL (argument dependent lookup)

From cppreference:

Argument-dependent lookup, also known as ADL, or Koenig lookup, is the set of rules for looking up the unqualified function names in function-call expressions, including implicit function calls to overloaded operators. These function names are looked up in the namespaces of their arguments in addition to the scopes and namespaces considered by the usual unqualified name lookup.

Argument-dependent lookup makes it possible to use operators defined in a different namespace.

In your example a2 being from the namespace ns is enough for the compiler to also consider ns when looking for print.

The interesting part of your example is that int_type is also coming from ns, though it is just a typedef and int is not declared in ns. Consider that a typedef does not introduce a new type (rather an alias). So a2 really is an int.

PS: This is not specific to Visual studio. Any compiler conforming to the standard should accept the code posted.

Why is using namespace std; considered bad practice?

Consider two libraries called Foo and Bar:

using namespace foo;
using namespace bar;

Everything works fine, and you can call Blah() from Foo and Quux() from Bar without problems. But one day you upgrade to a new version of Foo 2.0, which now offers a function called Quux(). Now you've got a conflict: Both Foo 2.0 and Bar import Quux() into your global namespace. This is going to take some effort to fix, especially if the function parameters happen to match.

If you had used foo::Blah() and bar::Quux(), then the introduction of foo::Quux() would have been a non-event.

Namespace-scoped template function becomes visible in another namespace

That's argument-dependent lookup (ADL), also known as Koenig lookup.

In short, use of an operator or unadorned function call will find names in the enclosing namespaces of the operands or arguments.

Compiler behavior with const static non integral data initialization, namespace vs struct

Moreover, I think that this small example of namespace misuse could help us to shed some light on your first question (at least for the namespace case):

StaticNamespace.hpp:

#pragma once
namespace StaticNamespace
{
static double d = 1.0;
}

Class.hpp:

#include "StaticNamespace.hpp"
#include <iostream>

class Class
{

public:

#if 1

Class();

void printFromSource() const;

#else

Class(){
StaticNamespace::d = StaticNamespace::d + 0.1;
}

#endif

void printFromHeader() const { std::cout<<"Class from header "<<StaticNamespace::d<<" "<<&StaticNamespace::d<<std::endl; }

static void printDouble() { std::cout<<"Class static "<<StaticNamespace::d<<" "<<&StaticNamespace::d<<std::endl; }

};

Class.cpp:

#include "Class.hpp"

Class::Class()
{
StaticNamespace::d = StaticNamespace::d + 0.1;
}

void Class::printFromSource() const
{
std::cout<<"Class from source "<<StaticNamespace::d<<" "<<&StaticNamespace::d<<std::endl;
}

main.cpp:

#include <iostream>
#include "Class.hpp"

int main ()
{
Class test_class;

test_class.printFromHeader();

#if 1
test_class.printFromSource();
#endif

Class::printDouble();
}

If you set the preprocessor ifs to true you will have 2 translation units, otherwise there will be just one. As you can see the different behavior of the code in the two cases is compatible to the fact that each translation unit in this example owns an independent copy of the static variable. This is just a trivial example of course...

c++17 namespaces, is it possible to force qualified access ALWAYS?

A) is there any way to prevent all objects in a namespace to be blind to its fellow constituents, and to require access always through :: ?

There isn't.

B) if not, what would be the correct coding practice to obtain the desired behavior?

Use a class.

C) if not, an alternative solution would be to separate the aaa namespace into 2, one for functions and another for variables, like f_aaa and v_aaa. but this seems quite clunky and ugly in practical use, eg. void f_sqlite::myfun() { v_sqlite::myvar; } instead of just void sqlite::myfun() { sqlite::myvar; }

Use a class to group relevant functions and data together. What You want to hide put in a private section.

Why does endl(std::cout) compile

This behavior is called argument dependent lookup or Koenig lookup. This algorithm tells the compiler to not just look at local scope, but also the namespaces that contain the argument's type while looking for unqualified function call.

For ex:

namespace foo {
struct bar{
int a;
};

void baz(struct bar) {
...
}
};

int main() {
foo::bar b = {42};
baz(b); // Also look in foo namespace (foo::baz)
// because type of argument(b) is in namespace foo
}

About the piece of code referred in question text:

endl or std::endl is declared in std namespace as following:

template< class CharT, class Traits >
std::basic_ostream<charT,traits>& endl( std::basic_ostream<CharT, Traits>& os );

or

std::ostream& endl (std::ostream& os);

And cout or std::cout is declared as

extern std::ostream cout;

So calling std::endl(std::cout); is perfectly fine.

Now when one calls just endl(std::cout);, because the type of argument cout is from std namespace, unqualified supposedly a function endl is searched in std namespace and it is found succesfully and confirmed to be a function and thus a call to qualified function std::endl is made.


Further reading:

  1. GOTW 30: Name Lookup

  2. Why does 'std::endl' require the namespace qualification when used in the statement 'std::cout << std::endl;", given argument-dependent lookup?



Related Topics



Leave a reply



Submit