banner
cells

cells

为美好的世界献上 bug

命名空间那些坑

命名空间 & 源文件#

观察以下代码:

// 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_

在源文件中实现头文件中的定义,可以在实现的名称前加上 mymuduolib::currentthread:: 前缀以指定命名空间:

// 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));
    }
}

如果不添加命名空间前缀,就相当于有两个 t_cached_tidcachedTid(), 一个在 mymuduo::currentthread:: 命名空间,另一个在 :: 命名空间,且 mymuduo::currentthread:: 命名空间中的定义未给出实现,如果此时在某个模块中使用 using namespace mymuduolib::currentthread; 则会调用 mymuduolib::currentthread:: 命名空间下的 cachedTid()。(当然这段代码如果不添加命名空间前缀是无法通过编译的,因为函数中的变量 t_cached_tid 就会出现指定不明的问题)。

也可以将实现代码放在与头文件相同的命名空间下:

// 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

但是这里不能只使用 using namespace mymuduolib::currentthread;,因为函数和变量实现的作用域需要明确指定,如未指定相当于之后的实现内容是独立于该命名空间。

但类中成员在实现时,可以使用 using namespace 的命名空间:

// poller.h
#ifndef _POLLER_H_
#define _POLLER_H_

namespace mymuduolib {

namespace net {

class Poller {
    Poller();
};

}

} // namespace net

#endif // _POLLER_H_

在源文件中:

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

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

using namespace mymuduolib::net;

Poller::Poller() {}

命名空间 & 前置声明#

前置声明可以避免循环依赖的问题,当前置声明遇到命名空间时有一个容易踩的坑。

观察以下错误代码:

// 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());
}

错误信息:

http_connect.cpp:6:1: 错误: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: 附注:备选为: ‘constexpr net::HttpConnect::HttpConnect(net::HttpConnect&&)
    8 | class HttpConnect {
      |       ^~~~~~~~~~~
http_connect.h:8:7: 附注:         ‘constexpr net::HttpConnect::HttpConnect(const net::HttpConnect&)
http_connect.h:10:5: 附注:         ‘net::HttpConnect::HttpConnect(const Addr&)
   10 |     HttpConnect(const Addr &);
      |     ^~~~~~~~~~~
http_connect.h:8:7: 附注:‘class net::HttpConnect’ defined here
    8 | class HttpConnect {
      |       ^~~~~~~~~~~

看到这个错误你可能会说是 using namespace 所导致的,其实并不是,使用 net::HttpConnect::HttpConnect(const net::Addr&) 也是一样的结果。

这里其实是前置声明上的问题,我们需要将前置声明 class Addr 放置 namespace net 命名空间内,因为前置声明的 class Addr:: 作用域中是不存在的。

// 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_

如有纰漏,欢迎指正。

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。