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

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

新規登録して質問してみよう
ただいま回答率
85.50%
C++

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

Q&A

解決済

2回答

1861閲覧

[C++] 親クラスの static const 変数を子クラスから情報を与えて初期化したい

tails

総合スコア22

C++

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

0グッド

0クリップ

投稿2020/06/28 11:50

コード

C++

1template<class Derived> 2class Base { 3private: 4 static const std::vector<const char*> paths; 5} 6 7class Derived1 : public Base<Derived1> { 8private: 9 static constexpr const char *path = "どこかのディレクトリへのパス1"; 10} 11class Derived2 : public Base<Derived2> { 12private: 13 static constexpr const char *path = "どこかのディレクトリへのパス2"; 14}

質問

上述のコードにおける Base::paths を Derived::path の中にあるファイル一覧で固定したいなと思っています。
探索は、実行時の一番初め、または、初めての実体化時に一度だけにしたいです。
(実行時の一番初めにできたら最もスマート)
paths の const は外したくないです。
…不可能でしょうか??

うまくいかない例1

C++

1template<class Derived> 2class Base { 3private: 4 static const std::vector<const char*> paths = [](){ 5 std::vector<const char*> paths; 6 7 // Derived::path がディレクトリ、path がその中のファイルとする。疑似コード。 8 for (path : Derived::path) { 9 paths.push_back(path); 10 } 11 12 return paths; 13 }(); // このラムダはコンパイル時定数を返すわけではないので、エラー 14} 15 16class Derived : public Base<Derived> { 17private: 18 static constexpr const char *path = "どこかのディレクトリへのパス"; 19}

うまくいかない例2

C++

1template<const char* path> 2class Base { 3private: 4 static const std::vector<const char*> paths = [](){ 5 std::vector<const char*> paths; 6 7 for (filename : Derived::path) { 8 paths.push_back(filename); 9 } 10 11 return paths; 12 }(); 13}; 14 15// Derived は path を使わないので、これで出来ればこれでも良いが… 16class Derived : public Base<"どこかのディレクトリへのパス"> { 17};

うまくいくけど、Derived のインスタンスを生成するたびに、ディレクトリを捜索してしまう例

C++

1template<class Derived> 2class Base { 3private: 4 const std::vector<const char*> paths = [](){ 5 std::vector<const char*> paths; 6 7 // Derived::path がディレクトリ、path がその中のファイルとする。疑似コード。 8 for (path : Derived::path) { 9 paths.push_back(path); 10 } 11 12 return paths; 13 }(); // このラムダはコンパイル時定数を返すわけではないので、エラー 14} 15 16class Derived : public Base<Derived> { 17private: 18 static constexpr const char *path = "どこかのディレクトリへのパス1"; 19}

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

こんにちは。

「うまくいかない例1」はクラス内初期化しようとしているからではないでしょうか?
constはコンパイル時定数までは要求しないです。constは通常は初期化後に変更できないの意味です。

inline staticならクラウ内で定義・初期化できます。ラムダ式でも初期化できるようですね。びっくりです。

C++

1#include <iostream> 2#include <vector> 3 4template<class Derived> 5class Base { 6public: 7 inline static const std::vector<const char*> paths = [](){ 8 std::vector<const char*> paths; 9 10 // Derived::path がディレクトリ、path がその中のファイルとする。疑似コード。 11// for (path : Derived::path) { 12 paths.push_back(Derived::path); 13 paths.push_back(Derived::path); 14 paths.push_back(Derived::path); 15// } 16 17 return paths; 18 }(); // このラムダはコンパイル時定数を返すわけではないので、エラー 19}; 20 21class Derived : public Base<Derived> { 22public: 23 static constexpr const char *path = "どこかのディレクトリへのパス"; 24}; 25 26int main() 27{ 28 for (auto const* p : Derived::paths) 29 std::cout << p << "\n"; 30 31}

wandbox


【コメントを見て追記】
試しに VC++ 2017で上記ソースをコンパイルしてみたら、おっしゃる通りのエラーがでました。

上記回答の前に実はinlineとラムダ式を使わないバージョンを作っていたのですが、そちらは通りました。

C++

1#include <iostream> 2#include <vector> 3 4template<class Derived> 5class Base { 6private: 7 static const std::vector<const char*> paths; 8 9 static std::vector<const char*> init() 10 { 11 std::cout << "init()\n"; 12 std::vector<const char*> paths; 13 14 // Derived::path がディレクトリ、path がその中のファイルとする。疑似コード。 15 paths.push_back(Derived::path); 16 paths.push_back(Derived::path); 17 paths.push_back(Derived::path); 18 19 return paths; 20 } 21protected: 22 const std::vector<const char*> getPaths() const { return paths; } 23}; 24 25template<class Derived> 26const std::vector<const char*> Base<Derived>::paths=Base<Derived>::init(); 27 28class Derived : public Base<Derived> { 29 friend class Base<Derived>; 30private: 31 static constexpr const char *path = "どこかのディレクトリへのパス"; 32public: 33 void print() 34 { 35 for (auto const* p : getPaths()) 36 { 37 std::cout << p << "\n"; 38 } 39 } 40}; 41 42int main() 43{ 44 std::cout << "main()\n"; 45 Derived derived; 46 derived.print(); 47}

wandbox

init()はmain()の前に走っていますが、たまたまかも知れません。
ただ、確かboostは、使っているように見せかけることでmain()より前に走らせていましたので、もしかするとある程度は当てにして良いかもしれません。

投稿2020/06/28 15:05

編集2020/06/28 16:52
Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tails

2020/06/28 15:57

// このラムダはコンパイル時定数を返すわけではないので、エラー この説明はなんか、伝えたいことがうまく伝えられませんでした、すみません。 そのようにしたいのですが、手元の環境(Visual Studio 2019)では、Derived::path が見つけられないようで、エラーになってしまいます… 継承  : public Base<Derived> の時点では、Derived は不完全型なので、分からないのかな、と思います。 一応、エラー内容は、'path': 'Derived' のメンバーではありません です。
Chironian

2020/06/28 16:23

Derived::pathはprivateに入っているようです。私は一旦publicへ変更しました。 この辺りは大丈夫ですか?
Chironian

2020/06/28 16:49

その後、VC++でやってみたら、おっしゃる通りでした。回答に追記します。
tails

2020/06/29 01:12

inline だと、その時点で Derived::path が見えないといけないので、ダメみたいですね… inline ではなく、外部にラムダ式の呼び出しで定義することで、無事コンパイルが通りました。 ありがとうございました!
guest

0

シングルトンパターンを適用すると初期化を1回に抑えることができます。

シングルトンのベターな実装方法

[追記] 自分なりに不細工なコードを書いてみました。

C++

1#include <iostream> 2#include <vector> 3#include <map> 4#include <string> 5 6static std::vector<std::string> getPaths(size_t typeHash, const std::string typeName, const std::string path) { 7 static std::map<std::size_t, std::vector<std::string>> m; 8 if (m.count(typeHash) == 0) { 9 std::cout << "DEBUG: 呼び出されたよん" << std::endl; 10 std::vector<std::string> v; 11 v.push_back(std::string() + typeName + "の" + path + "でなんか適当なやつ1"); 12 v.push_back(std::string() + typeName + "の" + path + "でなんか適当なやつ2"); 13 v.push_back(std::string() + typeName + "の" + path + "でなんか適当なやつ3"); 14 m.insert(std::make_pair(typeHash, v)); 15 } 16 return m[typeHash]; 17} 18 19class Base { 20protected: 21 std::vector<std::string> paths; 22 23public: 24 Base(size_t typeHash, std::string typeName, std::string path) { 25 paths = getPaths(typeHash, typeName, path); 26 }; 27 28 void printPaths() { 29 for (auto p: paths) 30 std::cout << p << std::endl; 31 }; 32}; 33 34class A : public Base { 35 public: A() : Base(typeid(this).hash_code(), typeid(this).name(), std::string("Path1")) { }; 36}; 37class B : public Base { 38 public: B() : Base(typeid(this).hash_code(), typeid(this).name(), std::string("Path2")) { }; 39}; 40 41 42int main() { 43 { 44 A a; 45 a.printPaths(); 46 } 47 { 48 B b; 49 b.printPaths(); 50 } 51 { 52 A a; 53 a.printPaths(); 54 } 55 return 0; 56}

投稿2020/06/28 12:47

編集2020/06/28 15:37
anndonut

総合スコア667

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tails

2020/06/28 12:59

インスタンスは複数生成できる必要があるので、シングルトンパターンは嫌です…
tails

2020/06/28 16:01

なるほど… 確かに、それなら実現できますね! static な変数をラムダ式として持たせて、paths を初期化しても同じようにできそうです! ありがとうございます。 (コンストラクタに static な変数を持たせることしか思いつけませんでした…)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問