名前空間とソースファイル#
以下のコードを観察してください:
// 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) {
// Linuxシステムコールを使用して現在のIDを取得する
t_cached_tid = static_cast<pid_t>(::syscall(SYS_gettid));
}
}
名前空間の接頭辞を追加しない場合、mymuduo::currentthread::
の名前空間と ::
の名前空間の 2 つの t_cached_tid
と cachedTid()
が存在することになります。また、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) {
// Linuxシステムコールを使用して現在のIDを取得する
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_
もし間違いがあれば、指摘してください。