69498


test2.hに宣言されている名前空間Bでtest1.hに宣言されている名前空間Aに宣言されたクラスをメンバとしているのですが,
以下のようなエラーが出ます.

test1.hでの `#include"test2.h"` を消せば上手く行くのは分かったのですが, なぜ上手く行くのかが分からないです.
よろしくお願いします.

**エラーメッセージ**

[code]

        test2.h test1.h
    In file included from test1.h:3:0,
                     from test1.cpp:1,
                     from test3.cpp:2:
    test2.h:7:5: error: ‘A’ does not name a type
         A::K t;
         ^
    In file included from test3.cpp:3:0:
    test2.cpp: In function ‘void B::Func()’:
    test2.cpp:3:2: error: ‘t’ was not declared in this scope
      t.k=100;
      ^
    test2.cpp:3:2: note: suggested alternative: ‘tm’
      t.k=100;
      ^
      tm
    In file included from test1.h:3:0:
    test2.h:7:5: error: ‘A’ does not name a type
         A::K t;
    
[/code]

### ソースコード

test1.h

[code]

    #ifndef test1_H_
    #define test1_H_
    #include<iostream>
    #include"test2.h"
    namespace A{
     class K{
      public:
      int k;
      void main();
     };
    };
    #endif
    
[/code]

test1.cpp

[code]

    #include"test1.h"
    void A::K::main(){
     std::cout << A::K::k << std::endl;
    }
    
[/code]

test2.h

[code]

    #ifndef test2_H_
    #define test2_H_
    #include<iostream>
    #include"test1.h"
    namespace B{
     A::K t;
     void Func();
    };
    #endif
    
[/code]

test2.cpp

[code]

    #include"test2.h"
    void B::Func(){
     t.k=100;
     t.main();
    }
    
[/code]

test3.cpp

[code]

    #include<iostream>
    #include"test1.cpp"
    #include"test1.h"
    #include"test2.cpp"
    #include"test2.h"
    int main(){
     B::Func();
    }
    
[/code]


CC BY-SA 4.0
2020-08-12T10:25:27.803
69487
2022-11-07T12:46:43.887
2022-11-07T12:46:43.887
4236
41456
question
c++ preprocessor
c++での分割コンパイル


`test3.cpp`の`#include`の内容を忠実に追えばわかることです。

[code]

    #include<iostream>   // これは質問と関係ないので追わない
    #include"test1.cpp"
      #include"test1.h"
        #ifndef test1_H_
        #define test1_H_       // test1_H_がここで定義される
        #include<iostream>     // これは質問と関係ないので追わない
        #include"test2.h"
          #ifndef test2_H_
          #define test2_H_
          #include<iostream>   // これは質問と関係ないので追わない
          #include"test1.h"
            #ifndef test1_H_   // test1_H_は定義済みなので#endifまで無視される
            #endif
          namespace B{
           A::K t;             // ここより上にAに関する宣言が存在しない
           void Func();
          };
          #endif
        namespace A{
         class K{
          public:
          int k;
          void main();
         };
        };
        #endif
      void A::K::main(){
        std::cout << A::K::k << std::endl;
      }
    #include"test1.h"
    #include"test2.cpp"
    #include"test2.h"
    int main(){
     B::Func();
    }
    
[/code]

見て明らかなように、 `A::K t;` に到達した時点で、 `A` に関する宣言・定義いずれもありません。ですのでエラーになります。

* * *

というように、`#include`はいい感じに解決してくれる魔法のキーワードというわけではなく、愚直に指定された行に指定されたファイルの内容をインクルードする機能です。  
逆に言えば、`#include`結果は開発者にも正確に理解できる機能であり、test3.cppで書かれた

[code]

    #include"test1.cpp"
    #include"test1.h"
    #include"test2.cpp"
    #include"test2.h"`
    
[/code]

のように何でもかんでも`#include`すれば問題が解決できるなどと考えるべきではありません。もっとご自身の書いたソースコードに目を向けましょう。


CC BY-SA 4.0
2020-08-12T11:35:45.853
69492
2020-08-12T22:03:39.190
2020-08-12T22:03:39.190
4236
4236
69487
answer


うまく行かない理由は、先に回答されている方の書かれた通りなのですが、では、どう書くのが正しいのか、について書いてみたいと思います。

ヘッダファイル test1.h と test2.h のように複数のヘッダファイルを作っていく場合には、依存関係が単一方向になるように設計する必要があります。

test1.h の先頭で test2.h
をincludeするのであれば、test2.hの方ではtest1.hをincludeしてはいけません。逆もしかりです。

今回のヘッダファイルの内容を見ると、test2.hの記載内容をコンパイラが理解するためにはtest1.hの内容が必要ですが、test1.hをコンパイルするためにはtest2.hの内容をコンパイラが先に見ておく必要はありません。

ですので、test2.hからtest1.hをインクルードするようにします。

次にソースファイルですが、ソースファイルからソースファイルをインクルードすることはしません。

この例題ではソースファイルがtest1.cppと
test2.cppとtest3.cppの3つしか登場しませんが、実際のプロジェクトではソースファイルが何千とか何万というのはざらです。そのような状況では１つ１つのソースファイルを別々にコンパイルします。  
そのようにして何がうれしいかというと、一部のソースに加筆や修正を行ったときに、修正した一部のソースファイルだけコンパイルすることが可能になります。

今回のtest3.cppのように中心となる１つのソースファイルから、残りのソースファイルを全部includeしてしまっては、分割した意味がなくなってしまいます。

ですので、test3.cppから他のソースをincludeするのはやめて、test1.cpp、test2.cpp、test3.cppはそれぞれ独立にコンパイルして、最後にリンクします。

コンパイル操作はたとえば次のようにします。

[code]

    $ g++ -c test1.cpp
    $ g++ -c test2.cpp
    $ g++ -c test3.cpp
    $ g++ -o test123 test1.o test2.o test3.o
    
[/code]

こんなのを毎回手で打っていてはたまらないのでツールで自動化します。

test1.cppのソースファイルをコンパイルするのに必要なヘッダは test1.h だけですので、test1.cppからは test1.h
だけをincludeします。必要ないものまでincludeするとコンパイル時間がそれだけ余計にかかります。

test2.cpp, test3.cpp についても同様です。

ヘッダもソースも、必要最小限のヘッダファイルをincludeするように設計します。

# 追記

ときには、２つのヘッダに、お互いをincludeさせたくなることがあります。

典型的には、２つの構造体(struct)またはクラスが、互いを参照しているようなケースです。

たとえば、グラフ構造を表現するプログラムを書いているときに、節点(Node)と枝（Edge)のクラスがあって、Nodeの定義の中にEdgeを登場させたい、一方でEdgeの定義の中でNodeを登場させたい、ということがあったとしましょう。

コンパイラはファイルを先頭から最後まで順に処理するので、NodeとEdgeのどちらを定義するのか、決めないといけません。Nodeを先に書いたとしましょう。

そうしたら、Nodeの中でEdgeを参照したいときには、Edgeの定義をまだ読み込んでいません。

このような場合に使える技として、「定義」ではなく「宣言」を先に済ませる、というのがあります。

次のように書きます。

[code]

    class Node; // Nodeの宣言。定義は後ほど登場する。
                // 宣言により、"Node"がコンパイラに型名として認知される。
    
    class Edge { // Edge の定義。Edgeの内容を記述する。
    public:
       // Node の宣言しか、コンパイラは見ていない。
       // この状況でも可能な操作は、Nodeのポインタ型の変数の定義。
       // どの型のポインタも同じサイズなので、何かのポインタ、という情報だけで、
       // コンパイラは記憶領域のサイズを決定したり、コピー処理の命令を作り出せる
       // それ以上のことは、何もできない。
       Node *getSourceNode();
       Node *getDestinationNode();
       void setSourceNode(Node *n);
       void setDestinationNode(Node *n);
    private:
       Node *source_node_;
       Node *destination_node_;
       // ファイルを下まで読むと Node には inward_edge_count_ なるメンバが
       // 定義されていることがわかるが、この行を処理している時点ではコンパイラは
    　 // それを見ていないので、たとえば source_node_->inward_edge_count_ = 0;
       // などと、ここに書くと、コンパイルエラーになる。
    };
    
    // あとから、Nodeを定義する
    
    class Node {
    public:
      int getOutwardEdgeCount();
      Edge *getOutwardEdge(int edge_index);
      void addOutwardEdge(Edge *e);
      int getInwardEdgeCount();
      Edge *getInwardEdge(int edge_index);
      void addInwardEdge(Edge *e);
    private:
      // このNodeから出ていくedge
      int outward_edge_count_;
      Edge *outward_edges_[10]; // 最大10個まで記憶できる
      // このNodeに入ってくるedge
      int inward_edge_count_;
      Edge *inward_edges_[10];
    };
    
[/code]

このような書き方をすれば、相互に参照しあう型を定義できます。  
上記の２つのクラス NodeとEdgeは、互いに密接に関係していて、つねにセットで  
使われるでしょうから、典型的には１つのヘッダファイルに、２つのクラスを書きます。

NodeとEdgeを別々のヘッダファイルに分けて書くことも可能です。その場合には次のようにします。

  * Edge.h の中には Nodeの宣言とEdgeの定義を入れます。
  * Node.h の中には Edge.hのinclude指令と、Nodeの定義を入れます。

ファイル間の参照関係はあくまでも一方通行であり、どちらにどちらを参照させるのかは、設計者がしっかり決める必要があります。

C/C++はそういう言語です。

これがたとえばjavaだと相互に参照してもOKですね。コンパイラがファイルを読む読み方が、C/C++とはだいぶ違うので。


CC BY-SA 4.0
2020-08-12T14:08:20.653
69498
2020-08-14T09:17:30.663
2020-08-14T09:17:30.663
40200
40200
69487
answer