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) {
        // Linuxシステムコールを使用して現在のIDを取得する
        t_cached_tid = static_cast<pid_t>(::syscall(SYS_gettid));
    }
}

名前空間の接頭辞を追加しない場合、mymuduo::currentthread:: の名前空間と :: の名前空間の 2 つの t_cached_tidcachedTid() が存在することになります。また、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 Addrnamespace 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_

もし間違いがあれば、指摘してください。

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。