Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c++

namespaces

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."

like image 812
Grzegorz Szpetkowski Avatar asked Jul 09 '17 08:07

Grzegorz Szpetkowski


1 Answers

What's important to remember is that

  1. Function declarations in different namespaces don't interfere with each other.
  2. A definition of a function is also a declaration.
  3. [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.

like image 132
StoryTeller - Unslander Monica Avatar answered Nov 07 '22 09:11

StoryTeller - Unslander Monica