none
プロジェクトに追加されている定義ファイルをインクルードしなければならないとき RRS feed

  • 質問

  • あるヘッダに宣言されているクラスを、そのヘッダをインクルードして使用しており、それをビルドすると、

    プロジェクト内にそのヘッダの実装ファイルが存在しているにも関わらず、外部シンボルが未解決とのエラーが出ます。

    (開発環境は64bit版のVS 2015 Communityです)


    単純化した例で言えば、プロジェクト内に main.cpp, module.h, module.cpp の3つのファイルがあり、

    main.cpp では module.h をインクルードし、そこに宣言された関数などを呼び出しており、

    これをビルドすると、main.obj, module.obj は生成されるのですが、呼び出した関数について、外部シンボルが未解決とされてしまいます。

    結果的には、main.cpp 内で modue.cpp をインクルードすることでビルドは成功できたのですが、普段は実装ファイルをインクルードしなくてもビルドに成功する(当該.objファイルが勝手にリンクされる)ので、釈然としない部分があります。

    そこで、実装ファイルを明示的にインクルードしなければならないとき、しなくてもよいときがあれば、ぜひ教えていただきたいです。

    2016年8月25日 8:22

回答

  • 通常は、「実装ファイルを明示的にインクルードしなければならないとき」というのはあまりないと思います。

    回答ではなく、確認です。

    1. 実装ファイルは、ソリューションエクスプローラ内には存在していまでしょうか?
    2. ソリューションエクスプローラで実装ファイルを右クリックして、プロパティを表示させると、各構成や各プラットフォームに対して「ビルドから除外」の設定ができますが、これは「いいえ」(または空白)となっていますでしょうか?

    • 編集済み kenjinoteMVP 2016年8月25日 8:59
    • 回答としてマーク a18310 2016年8月25日 10:32
    2016年8月25日 8:43
  • >そこで、実装ファイルを明示的にインクルードしなければならないとき、しなくてもよいときがあれば、ぜひ教えていただきたいです。

    A.cpp、B.cpp、B.h が同一のプロジェクト内のメンバである場合、
    A.cppがB.hをインクルードすれば、B.cpp をインクルードしなければならないケースは「一般的にはありえません」。
    従って、「B.cppをA.cpp内でインクルードするとビルドが成功する」という現象は、

    1.B.h内の関数、ないしクラス内のメンバ関数の宣言と B.cpp内に実装している関数が異なっている。
    2.実装は正しいが、コンパイルオプションがA.cppとB.cppで異なるため、
     異なるインターフェースを持つ関数として認識されている。
     ないし、内部で使用される関数名称の修飾が、期待されているものと異なる。
     (例:__stdcall、__cdecl等の呼び出し規約の解釈の違いや、cコードとして解釈するかC++コードとして解釈するか等、色々)

    等が考えられます。
    まず、実装が異なっていないことを確認するために、
    できるのであれば、現象の発生するクラス、関数のコードを掲載した方が早いかもしれません。
    2.については、同一のコンパイルオプションが指定されていることを確認してみてはどうでしょう。

    最初に、module.cppと、main.cppインクルードしているmodule.hが「同一」であることの確認が必要かもしれません。
    これは、両cppが微妙に異なるヘッダーをインクルードしている可能性が排除できないからです。
    ヘッダーのインクルードはプロジェクトに含まれているかどうかとは「無関係」に、
    存在していればインクルードエラーにはならないので、注意が必要です。

    • 編集済み 仲澤@失業者 2016年8月25日 9:28
    • 回答としてマーク a18310 2016年8月25日 10:21
    • 回答としてマークされていない a18310 2016年8月25日 10:21
    • 回答としてマーク a18310 2016年8月25日 10:32
    2016年8月25日 9:13

すべての返信

  • 通常は、「実装ファイルを明示的にインクルードしなければならないとき」というのはあまりないと思います。

    回答ではなく、確認です。

    1. 実装ファイルは、ソリューションエクスプローラ内には存在していまでしょうか?
    2. ソリューションエクスプローラで実装ファイルを右クリックして、プロパティを表示させると、各構成や各プラットフォームに対して「ビルドから除外」の設定ができますが、これは「いいえ」(または空白)となっていますでしょうか?

    • 編集済み kenjinoteMVP 2016年8月25日 8:59
    • 回答としてマーク a18310 2016年8月25日 10:32
    2016年8月25日 8:43
  • >そこで、実装ファイルを明示的にインクルードしなければならないとき、しなくてもよいときがあれば、ぜひ教えていただきたいです。

    A.cpp、B.cpp、B.h が同一のプロジェクト内のメンバである場合、
    A.cppがB.hをインクルードすれば、B.cpp をインクルードしなければならないケースは「一般的にはありえません」。
    従って、「B.cppをA.cpp内でインクルードするとビルドが成功する」という現象は、

    1.B.h内の関数、ないしクラス内のメンバ関数の宣言と B.cpp内に実装している関数が異なっている。
    2.実装は正しいが、コンパイルオプションがA.cppとB.cppで異なるため、
     異なるインターフェースを持つ関数として認識されている。
     ないし、内部で使用される関数名称の修飾が、期待されているものと異なる。
     (例:__stdcall、__cdecl等の呼び出し規約の解釈の違いや、cコードとして解釈するかC++コードとして解釈するか等、色々)

    等が考えられます。
    まず、実装が異なっていないことを確認するために、
    できるのであれば、現象の発生するクラス、関数のコードを掲載した方が早いかもしれません。
    2.については、同一のコンパイルオプションが指定されていることを確認してみてはどうでしょう。

    最初に、module.cppと、main.cppインクルードしているmodule.hが「同一」であることの確認が必要かもしれません。
    これは、両cppが微妙に異なるヘッダーをインクルードしている可能性が排除できないからです。
    ヘッダーのインクルードはプロジェクトに含まれているかどうかとは「無関係」に、
    存在していればインクルードエラーにはならないので、注意が必要です。

    • 編集済み 仲澤@失業者 2016年8月25日 9:28
    • 回答としてマーク a18310 2016年8月25日 10:21
    • 回答としてマークされていない a18310 2016年8月25日 10:21
    • 回答としてマーク a18310 2016年8月25日 10:32
    2016年8月25日 9:13
  • kenjinote様、仲澤様、ご返信ありがとうございます。
    実装ファイルをインクルードする必要がないとの確信を得られて安心しました。

    おかげさまで、この現象が起こる条件が分かりました。
    結論から言うと、異なるディレクトリに同名のソースファイルが存在したため、出力されるオブジェクトファイル名が重なってしまい、それでリンクが正常にできなかったものと思われます。
    上記の対処で一時的に解決したのは、上書きされるオブジェクトファイルが含む実装を、上書きされない方のファイルに移したことが原因のようです。

    必要な部分だけ書くと、こういう構成になっていました。
    main.h
    main.cpp
    database.h
    database.cpp
    lib/Database.h
    lib/Database.cpp
    (main.cppがすべてのヘッダをインクルード)

    インクルードファイルでは大文字小文字が区別されないことなどを失念して大分気づくのに時間が掛かってしまいましたが、何とか解決できました。
    お二方ともお忙しい中、ご協力いただきありがとうございました。

    2016年8月25日 10:20