[迷信] 非局所オブジェクトは外部結合

今回のタイトルはやや分かりにくいかもしれません。「非局所オブジェクト」というのは、関数の外で宣言したオブジェクトのことです。いわゆる「グローバル変数」とほぼ同じと考えてください。

さて、関数の外で宣言された「非局所オブジェクト」ですが、static 記憶クラス指定子が付いていれば内部結合になることはいうまでもありません。今回話題にするのは、記憶クラス指定子が付いていないデフォルトの状態での結合がどうなるかです。

結論からいうと、非局所オブジェクトが外部結合になるか内部結合になるかは、C と C++ では異なります。これは、C と C++ の間の重要な非互換性のひとつです。

まず、C の場合には、明示的に static 記憶クラス指定子を付けない限り、非局所オブジェクトは必ず外部結合になります。それに対して、C++ では、const 修飾子が付いた非局所オブジェクトは、デフォルトでは内部結合になります。

では、const 修飾子と同じ仲間である volatile 修飾子はどうかというと、これは結合の種類には影響を与えません。const volatile のように両方の修飾子が付いた場合には、やはり const 修飾子が付いているので内部結合になります。

では、C++ では、どのように書けば const 修飾子のついた非局所オブジェクトを外部結合にできるのでしょうか? それは、次のようにします。

extern const int foo = 123;

上のように、明示的に extern 記憶クラス指定子を付けることで、const 修飾子が付いていても外部結合にすることができます。C では、extern 記憶クラス指定子は(定義ではなく)宣言だけのために、主にヘッダファイルで使っていたと思いますが、C++ では、このように定義の際にも extern 記憶クラス指定子を記述する必要が出てきます。

なお、単なる外部結合ではなく、C 結合にする場合には、

extern "C" const int foo = 123;

のようにしなければなりません。

ところで、非局所オブジェクトを外部結合にする場合には、通常ヘッダファイルの中でも宣言を行うかと思います。その場合、

// foo.hpp
extern const int foo;

// foo.cpp
const int foo = 123;

のように、定義時には extern 記憶クラス指定子を省略することも可能です。ただし、(上の例で言えば)foo.cpp から foo.hpp がインクルードされていることが絶対条件です。

これまでの例は比較的簡単でした。しかし、実際にはもっと判断しにくいケースもあります。

const char s1[] = "abc";
const char* s2 = "def";

上のコードに出てくる s1s2 は、それぞれ外部結合でしょうか? それとも内部結合でしょうか?

正解は、s1 は内部結合であり、s2 は外部結合です。s2 は確かに const 修飾子が付いていますが、それはあくまでも参照先の型についてであり、ポインタ型自体が const で修飾されているわけではありません。ですから、

const char* const s3 = "ghi";

のようにすれば、s3 は内部結合になります。

C または C++ しか使わなければ、この非互換性に悩まされることはあまりないでしょう。しかし、両方の言語を使い分けなければならないとき、油断していると、ついついはまってしまう落とし穴です。

このエントリーを含むはてなブックマーク