質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
86.12%
GCC

GCCはGNU Compiler Collectionの略です。LinuxのC言語コンパイラのデファクトスタンダードであり、数多くの他言語やプラットフォームサポートもします。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

解決済

C++で2つの構造体の定義がお互いの定義を必要としている場合の解決方法

Soybeanman
Soybeanman

総合スコア15

GCC

GCCはGNU Compiler Collectionの略です。LinuxのC言語コンパイラのデファクトスタンダードであり、数多くの他言語やプラットフォームサポートもします。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

1回答

0グッド

0クリップ

315閲覧

投稿2022/10/30 11:44

前提

C++のプログラム内で、ある2つの構造体A,Bを定義しました。
構造体Aではコンストラクタで構造体Bの静的メンバ関数を必要としています。一方、構造体Bでは静的メンバ変数、関数定義で構造体Aの定義を必要としています。
そういった場合、どのように実装して相互参照の状態を解消すれば良いのでしょうか。

実現したいこと

  • 簡潔に構造体A,Bをエラーなく定義する。

発生している問題・エラーメッセージ

cannot convert 'A*' to 'int*'(A.hppのB::Append(this);の行) 'A' was not declared in this scope(B.hppのstatic inline std::vector<A*> As{};の行) など

該当のソースコード

C++:main.cpp

1#include <stdio.h> 2#include "A.hpp" 3#include "B.hpp" 4 5int main(){ 6 // 適当な処理 7}

C++:A.hpp

1#pragma once 2 3struct A 4{ 5 A(){ 6 B::Append(this); 7 } 8 9 void update(){ 10 // ここに更新処理 11 } 12};

C++:B.hpp

1#pragma once 2#include <vector> 3 4struct B 5{ 6 static inline std::vector<A*> As{}; 7 8 static void Append(A* a) { 9 As.push_back(a); 10 } 11 12 static void UpdateAll() { 13 for(auto& a : As){ 14 a->update(); 15 } 16 } 17};

試したこと

一応の解決策として、Aの派生クラス(Cとする)を作ることが挙げられます。

C++:A.hpp

1#pragma once 2 3struct A 4{ 5 A(){ 6 // ここにコンストラクタの処理 7 } 8 9 void update(){ 10 // ここに更新処理 11 } 12};

C++:B.hpp

1#pragma once 2#include <vector> 3#include "A.hpp" 4 5struct B 6{ 7 static inline std::vector<A*> As{}; 8 9 static void Append(A* a) { 10 As.push_back(a); 11 } 12 13 static void UpdateAll() { 14 for(auto& a : As){ 15 a->update(); 16 } 17 } 18};

C++:C.hpp

1#pragma once 2#include "A.hpp" 3#include "B.hpp" 4 5struct C : A 6{ 7 C() : A(){ 8 B::Append(this); 9 } 10};

しかし、B::Append()のためだけに派生クラスを作るのは少々簡潔ではないような気がします。
ですので、以下のような方法で解決したいと考えています。

1.仮の定義を書いてコンパイルを通す。
具体的には、A.hpp、B.hppの構造定義の前にB,Aの仮の構造体の定義を書いて、後々詳細な定義がわかるとコンパイラに伝えて解決したいと考えています。

2.構造体Bの定義文の後に、コンストラクタA()の定義をする。
具体的には、A.hpp内ではコンストラクタA()の定義をせずに(あるいは置き換え可能な仮の定義を置いて)、B.hppのBの定義が終わった後にコンストラクタA()の詳細な定義をして解決したいと考えています。

以上のようなことを行える構文、あるいは別の簡潔な構文がありましたらご教授いただけると幸いです。

補足情報(FW/ツールのバージョンなど)

エディタ:VSCode バージョン 1.72.2
コンパイラ:gcc バージョン 9.2.0
C++17(tasks.jsonのargsに"-std=c++17"を指定して実行。Aの派生クラスCを作った場合では正しく実行されました。)

ご不明な点がございましたら、お手数ですがコメントまでお知らせください。
何卒、ご教授お願いします。

以下のような質問にはグッドを送りましょう

  • 質問内容が明確
  • 自分も答えを知りたい
  • 質問者以外のユーザにも役立つ

グッドが多くついた質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

気になる質問をクリップする

クリップした質問は、後からいつでもマイページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

下記のような質問は推奨されていません。

  • 間違っている
  • 質問になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

適切な質問に修正を依頼しましょう。

回答1

1

ベストアンサー

A,B が相互参照する場合、ヘッダだけで解決するのは無理。

// ---------- A.hpp ---------- #pragma once struct A { A(); void update(){ // ここに更新処理 } }; // ---------- A.cpp ---------- #include "A.hpp" #include "B.hpp" A::A(){ B::Append(this); } // ---------- B.hpp ---------- #pragma once #include <vector> #include "A.hpp" struct B { static inline std::vector<A*> As{}; static void Append(A* a) { As.push_back(a); } static void UpdateAll() { for(auto& a : As){ a->update(); } } }; // ---------- main.cpp ---------- #include <stdio.h> #include "A.hpp" #include "B.hpp" int main(){ // 適当な処理 }

投稿2022/10/30 11:58

episteme

総合スコア16005

Soybeanman😄を押しています

良いと思った回答にはグッドを送りましょう。
グッドが多くついた回答ほどページの上位に表示されるので、他の人が素晴らしい回答を見つけやすくなります。

下記のような回答は推奨されていません。

  • 間違っている回答
  • 質問の回答になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

このような回答には修正を依頼しましょう。

回答へのコメント

Soybeanman

2022/10/31 00:45

回答ありがとうございます!! 返信が遅れてしまい申し訳ございません。 ヘッダではなくcppファイルに分けてcppファイルにてコンストラクタを外付けで定義したわけですね! cppファイルではなく#pragma onceをつけたhppファイルでもできたのでそちらを採用しようと思います。 また、回答のコードのmain.cppのincludeパスでは動かなかったため、以下にこちらの環境で実行できたコードを示しておきます。 ```C++:main.cpp #include <stdio.h> #include "A.cpp" #include "B.hpp" int main(){ // 適当な処理 } ``` epistemeさんの回答をベストアンサーとして解決としたいと思います。 ありがとうございました!
episteme

2022/10/31 01:42

コンパイラがg++なら: g++ Main.cpp A.cpp でコンパイルできませんか? ~.cpp を #include するのはお行儀よくはなかろうと...
Soybeanman

2022/10/31 02:27

指摘ありがとうございます。 hppファイルのみをincludeする場合、main.cppにおけるmain()の中身がコメントのみの場合は通りましたが、以下のようにA,Bを使用する処理を書いた場合にはコンパイルに失敗しました。 int main(){ A a{}; B::UpdateAll(); std::cout << "OK\n"; // 適当な処理 } また、以下のようなエラーがVSCode上にて表示されました。 「preLaunch Task 'C/C++:g++.exe アクティブなファイルのビルド'が終了コード-1で終了しました。」 しかし、詳しいエラー内容を見ようと「エラーの表示」を押してみますが特にエラーはありませんでした。なので、VSCodeでの問題かもしれません。 このコメントのコードを以下のようにした場合には正しくコンパイル、動作しました。 ```C++:A.hpp #pragma once #include <iostream> struct A { A(); virtual void update(){ std::cout << "Update\n"; // ここに更新処理 } }; #include "B.hpp" A::A(){ B::Append(this); } ``` ```C++:main.hpp #include <iostream> #include "A.hpp" #include "B.hpp" int main(){ A a{}; B::UpdateAll(); std::cout << "OK\n"; // 適当な処理 } ``` このように書けばcppファイルをincludeすることなく書けそうです。 その他、何かご指摘がありましたらコメントにて書いていただけると幸いです。

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
86.12%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問

同じタグがついた質問を見る

GCC

GCCはGNU Compiler Collectionの略です。LinuxのC言語コンパイラのデファクトスタンダードであり、数多くの他言語やプラットフォームサポートもします。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。