banner
cells

cells

为美好的世界献上 code

Pitfalls of Namespaces

Namespace can effectively prevent naming conflicts, but if used improperly, it can also lead to issues such as ambiguous specification or redefinition.

Namespace & Source Files#

Consider the following code:

// current_thread.h
#ifndef _CURRENT_THREAD_H_
#define _CURRENT_THREAD_H_

namespace mymuduolib {

namespace currentthread {

extern __thread int t_cached_tid;

void cachedTid();

} // namespace currentthread

} // namespace mymuduolib

#endif // _CURRENT_THREAD_H_

To implement the definitions in the source file, you can add the mymuduolib::currentthread:: prefix before the implementation name to specify the namespace:

// current_thread.cpp
#include "current_thread.h"
#include <unistd.h>
#include <sys/syscall.h>

__thread int mymuduolib::currentthread::t_cached_tid = 0;

void mymuduo::currentthread::cachedTid() {
    if (t_cached_tid == 0) {
        // get the current id through Linux system call
        t_cached_tid = static_cast<pid_t>(::syscall(SYS_gettid));
    }
}

If the namespace prefix is not added, there will be two t_cached_tid variables and cachedTid() functions, one in the mymuduo::currentthread:: namespace and another in the :: namespace. The definition in the mymuduo::currentthread:: namespace is not implemented. If you use using namespace mymuduolib::currentthread; in a module, it will call the cachedTid() function in the mymuduolib::currentthread:: namespace. (Of course, this code will not compile without adding the namespace prefix because t_cached_tid will have an ambiguous specification).

You can also put the implementation code in the same namespace as the header file:

// current_thread.cpp
#include "current_thread.h"
#include <unistd.h>
#include <sys/syscall.h>

namespace mymuduolib {

namespace currentthread {

__thread int t_cached_tid = 0;

void cachedTid() {
    if (t_cached_tid == 0) {
        // get the current id through Linux system call
        t_cached_tid = static_cast<pid_t>(::syscall(SYS_gettid));
    }
}
    
} // namespace mymuduolib
    
} // namespace currentthread

But in this case, you cannot use using namespace mymuduolib::currentthread;. This statement means you can directly use functions, variables, and other members in this namespace without specifying the namespace. However, in the implementation, you are not using this namespace, so the implementation is independent of this namespace.

However, you can use using namespace for namespaces when implementing class members:

// poller.h
#ifndef _POLLER_H_
#define _POLLER_H_

namespace mymuduolib {

namespace net {

class Poller {
    Poller();
};

}

} // namespace net

#endif // _POLLER_H_

In the source file:

// poller.cpp
#include "poller.h"

// mymuduolib::net::Poller::Poller() {}

using namespace mymuduolib::net;

Poller::Poller() {}

Namespace & Forward Declaration#

Forward declaration can avoid circular dependency issues. However, there is a pitfall when encountering namespaces with forward declaration. Consider the following erroneous code:

// addr.h
#ifndef _ADDR_H_
#define _ADDR_H_

namespace net {

class Addr {};

} // namespace net

#endif // _ADDR_H_
// http_connect.h
#ifndef _HTTP_CONNECT_H_
#define _HTTP_CONNECT_H_

class Addr;

namespace net {

class HttpConnect {
public:
    HttpConnect(const Addr &);
};

} // namespace net

#endif // _HTTP_CONNECT_H_
// http_connect.cpp
#include "http_connect.h"
#include "addr.h"

using namespace net;

HttpConnect::HttpConnect(const Addr &) {}
// main.cpp
#include "http_connect.h"

int main() {
    net::HttpConnect(Addr());
}

Error message:

http_connect.cpp:6:1: error: no declaration matches ‘net::HttpConnect::HttpConnect(const net::Addr&)
    6 | HttpConnect::HttpConnect(const Addr &) {}
      | ^~~~~~~~~~~
In file included from http_connect.cpp:1:
http_connect.h:8:7: note: candidate is: ‘constexpr net::HttpConnect::HttpConnect(net::HttpConnect&&)
    8 | class HttpConnect {
      |       ^~~~~~~~~~~
http_connect.h:8:7: note:         ‘constexpr net::HttpConnect::HttpConnect(const net::HttpConnect&)
http_connect.h:10:5: note:         ‘net::HttpConnect::HttpConnect(const Addr&)
   10 |     HttpConnect(const Addr &);
      |     ^~~~~~~~~~~
http_connect.h:8:7: note: ‘class net::HttpConnect’ defined here
    8 | class HttpConnect {
      |       ^~~~~~~~~~~

You might think that this error is caused by using namespace, but it is not. Even using net::HttpConnect::HttpConnect(const net::Addr&) will yield the same result.

The issue here is actually with the forward declaration. We need to place the forward declaration class Addr; inside the namespace net namespace to resolve this error.

// http_connect.h
#ifndef _HTTP_CONNECT_H_
#define _HTTP_CONNECT_H_

namespace net {

class Addr;

class HttpConnect {
public:
    HttpConnect(const Addr &);
};

} // namespace net

#endif // _HTTP_CONNECT_H_

Summary#

Many of the previous examples implicitly illustrate the drawbacks of using using namespace. Therefore, it is best to avoid using this method globally to avoid polluting namespaces. However, it is not forbidden to use it; we can use it within a controlled scope, such as within a function or a block scope.

If there are any omissions, please let me know.

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.