名前空間の不思議

以前に CLX C++ Libraries のバグを指摘してくれたページが消えていたので,バックアップ代わりに.

まず,以下のコードは(少なくとも,VC++, cygwin+gcc 4.x.x では)コンパイルエラーとなります.

#include <cstdio>
#include <sstream>

namespace foo {
    /* --------------------------------------------------------------------- */
    /*
     *  puts
     *
     *  int 型の値を標準出力に出力させる(適当な)関数.
     *  同名の関数がグローバル名前空間に存在する.
     */
    /* --------------------------------------------------------------------- */
    int puts(int x) {
        std::stringstream ss;
        ss << x;
        return puts(ss.str().c_str());
    }
}

int main(int argc, char* argv[]) {
    const char* a = "Hello, world!";
    int x = 10;
    
    puts(a);
    foo::puts(x);
    
    return 0;
}
[example]$ g++ -Wall ex_namespace.cpp
ex_namespace.cpp: In function ‘int foo::puts(int)’:
ex_namespace.cpp:17: error: invalid conversion from ‘const char*’ to ‘int’
ex_namespace.cpp:17: error:   initializing argument 1 of ‘int foo::puts(int)’

foo::puts() が最後に標準関数である puts() を呼んで標準出力に出力させようとしているのですが,puts() が見つからないと言うコンパイルエラーが発生しています.どうやら,(引数の異なる)同名の関数が呼び元と同じ名前空間上に存在する場合,その名前空間より外で定義されている関数は検索しない事になっているようです(これが,例えば,foo::put_int() のような関数名で定義しておくと問題なくコンパイルできる).

この解決策としては,(同名の関数を作らないと言う対策を除くと)名前空間を明示すると言うものが挙げられます.例えば,上記の foo::puts() は以下のように修正するとコンパイルする事ができます.

namespace foo {
    int puts(int x) {
        std::stringstream ss;
        ss << x;
        
        // グローバル名前空間にある事を明示する.
        return ::puts(ss.str().c_str());
    }
}

以上のような問題があったため,これまではグローバル名前空間上に存在する関数を利用する際は全て "::" を付けるようにしていました.・・・が,この方針が仇となったケースが下記です.

以前に,CLX C++ Libraries のあるクラスを Mac 上でコンパイルするとコンパイルエラーが発生すると言う指摘を頂きました*1.そこではコンパイルエラーの原因についても調査してくれており,その原因は下記のようなものでした.

コンパイルエラーが発生していたのは,"::htons()" と言う記述(short 型の変数をホストバイトオーダーからネットワークバイトオーダーに変換するもの)でした.どうやら,Mac では htons() が関数ではなくマクロとして実装されているそうで,そのために "::" の記述が邪魔になっていたようです.

名前空間を扱う際のちょっとしたトラブルでした.

*1:正確には,リファラから辿ったらそう言う話が出ていた,ですが.