質問するログイン新規登録
オブジェクト指向

オブジェクト指向プログラミング(Object-oriented programming;OOP)は「オブジェクト」を使用するプログラミングの概念です。オブジェクト指向プログラムは、カプセル化(情報隠蔽)とポリモーフィズム(多態性)で構成されています。

デザインパターン

デザインパターンは、ソフトウェアのデザインでよく起きる問題に対して、解決策をノウハウとして蓄積し再利用出来るようにした設計パターンを指します。

C++

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

コーディング

コーディングは、実際にコードを書く工程に関する投稿。読みやすさ、効率的な実装、書き方の工夫、スタイルガイドに関する情報も含まれます。

Q&A

3回答

872閲覧

スタックの要素からスタックを操作したい場合の実装方法

fana

総合スコア12285

オブジェクト指向

オブジェクト指向プログラミング(Object-oriented programming;OOP)は「オブジェクト」を使用するプログラミングの概念です。オブジェクト指向プログラムは、カプセル化(情報隠蔽)とポリモーフィズム(多態性)で構成されています。

デザインパターン

デザインパターンは、ソフトウェアのデザインでよく起きる問題に対して、解決策をノウハウとして蓄積し再利用出来るようにした設計パターンを指します。

C++

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

コーディング

コーディングは、実際にコードを書く工程に関する投稿。読みやすさ、効率的な実装、書き方の工夫、スタイルガイドに関する情報も含まれます。

1グッド

2クリップ

投稿2025/09/09 05:11

編集2025/09/12 04:10

1

2

C++初心者です.

【問題があって → 一応これで問題回避できそうだけど……こんなのでいいのかなぁ? もっとマシなやり方があるのでは?】
みたいな,なんか漠然とした実装方法に関する疑問みたいな話なので,
意見交換とすべきか QA とすべきか迷いましたが,複数の方法論の話がある場合には個々の話が独立した形になった方が良い気がするので後者側にしました.

やりたいこと

なんかこんなの↓があって……

C++

1//何かスタックに積まれるやつ 2struct IStackElem 3{ 4 virtual ~IStackElem() = default; 5 6 //※この型は専らスタックに積まれる使い方をされるという前提であって, 7 //このメソッドが呼ばれるときとは,自身がスタックのtopである場合である. 8 virtual void DoSomething() = 0; 9}; 10 11//スタック 12using Stack_t = std::stack< std::unique_ptr<IStackElem> >;

何らかのタイミングでスタックの top にあるやつの DoSomething() を実施したい.

C++

1//どこかにスタックがある 2Stack_t TheStack; 3 4//何らかのタイミングでの処理 5void XXX() 6{ 7 /* ... */ 8 9 if( !TheStack.empty() )TheStack.top()->DoSomething(); 10 11 /* ... */ 12}

問題点

スタックに積まれている要素の DoSomething() 内でスタックに対する操作を行いたい のだが,
DoSomething() が呼ばれた際には自身がスタックの top だという前提なので)popした時点でその要素自身が破棄されてしまうという点で,実装方法に困っている.
例えば以下は「いくつかpopして→別のをpushする」みたいなのが実装できない状況を示している.

C++

1//(例) 2//DoSomething() にて, 3// まずスタックからいくつかの要素をpopし, 4// その後で新たな要素をpushする 5//ということをしたい. 6struct Problem : public IStackElem 7{ 8public: 9 //Owner : 所属スタック 10 //nPop : popする回数 11 //ToBePushed : pushすべき要素 12 Problem( Stack_t &Owner, int nPop, std::unique_ptr<IStackElem> ToBePushed ) 13 : m_Owner(Owner), m_nPop(nPop), m_ToBePushed(std::move(ToBePushed)) 14 {} 15 16 //ここの実装が問題(これだと実行時に大変なことになる) 17 virtual void DoSomething() override 18 {//m_nPop個だけ pop し,その後で m_ToBePushed を push したいのだが…… 19 20 //(1) pop 21 //m_nPop>=2 のとき,まずこれができない. 22 //(初回の pop で自身が破壊されるので m_Owner や m_nPop を使えない) 23 for( int i=0; i<m_nPop; ++i )m_Owner.pop(); 24 25 //(2) push 26 //こっちも同様に無理. 27 //(自身が破壊されているので m_ToBePushed を使えない) 28 m_Owner.push( std::move(m_ToBePushed) ); 29 }; 30 31private: 32 Stack_t &m_Owner; 33 int m_nPop; 34 std::unique_ptr<IStackElem> m_ToBePushed; 35};

こういう場合,どうすれば良いのでしょうか?

自身で思いつく問題回避方法は「 DoSomething() でやっていることを全て static メンバ関数に移してやる」なのですが,
もっと他の(何らかの意味で良い?)方法はありませんか?

C++

1 //思いつく問題回避方法 2 virtual void DoSomething() override 3 { 4 //処理を全て static メンバ関数に移した. 5 //必要なものを全て引数として渡して処理を実施する. 6 DoSomething_StaticImpl( m_Owner, m_nPop, std::move(m_ToBePushed) ); 7 }; 8 9private: //staticメンバ関数で処理 10 static void DoSomething_StaticImpl( Stack_t &Owner, int nPop, std::unique_ptr<IStackElem> ToBePushed ) 11 { 12 for( int i=0; i<nPop; ++i )Owner.pop(); 13 Owner.push( std::move( ToBePushed ) ); 14 }

追記

「そもそも要素側からスタックを操作すること自体がどうなのか?」というご指摘,もっともだと思います.
その方面(要素側からスタックを操作しないようにする)で考えると,こんな↓形になりました.
こちらに関しても「いや,そうじゃねぇよ」とか「やるにしてもこうだろう」等々ご教示願えればありがたいです.

C++

1//要素が直接スタックを操作するのではなくて, 2//要素からは「スタックをこのように操作したい」という要求を戻り値で返す. 3//(→で,戻り値を受け取った側でスタックを操作する) 4 5struct IStackElem; 6 7//スタック 8using Stack_t = std::stack< std::unique_ptr<IStackElem> >; 9 10//スタックに対するPush操作 11class Push 12{ 13public: 14 //ctorでPushすべきものを指定 15 Push( std::unique_ptr<IStackElem> ToBePushed ) : m_ToBePushed( std::move(ToBePushed) ) {} 16 //操作実施 17 void Exec( Stack_t &Stk ){ if(m_ToBePushed)Stk.push( std::move(m_ToBePushed) ); } 18private: 19 std::unique_ptr<IStackElem> m_ToBePushed; 20}; 21 22//スタックに対するPop操作 23class Pop 24{ 25public: 26 //ctorでPopすべき回数を指定 27 Pop( size_t nPop=1 ) : m_nPop(nPop) {} 28 //操作実施 29 void Exec( Stack_t &Stk ) 30 { 31 const size_t n = std::min( m_nPop, Stk.size() ); 32 for( size_t i=0; i<n; ++i )Stk.pop(); 33 } 34private: 35 size_t m_nPop; 36}; 37 38//スタックに対する操作要求 39using StackOp = std::variant< Push, Pop >; 40 41//スタックに積まれるやつ 42struct IStackElem 43{ 44 virtual ~IStackElem() = default; 45 46 //※この型は専らスタックに積まれる使い方をされるという前提であって, 47 //このメソッドが呼ばれるときとは,自身がスタックのtopである場合である. 48 // 49 //戻り値として,所属スタックに対して実施されるべき操作シーケンスを返す. 50 virtual std::vector<StackOp> DoSomething() = 0; 51}; 52 53//---------- 54//件の要素例の実装 55struct Problem : public IStackElem 56{ 57public: 58 Problem( size_t nPop, std::unique_ptr<IStackElem> ToBePushed ) 59 : m_nPop(nPop), m_ToBePushed(std::move(ToBePushed)) 60 {} 61 62 virtual std::vector<StackOp> DoSomething() override 63 {//m_nPop個だけ pop し,その後で m_ToBePushed を push したい 64 std::vector<StackOp> OPSeq; 65 OPSeq.emplace_back( Pop(m_nPop) ); 66 OPSeq.emplace_back( Push( std::move(m_ToBePushed) ) ); 67 return OPSeq; 68 }; 69 70private: 71 size_t m_nPop; 72 std::unique_ptr<IStackElem> m_ToBePushed; 73}; 74 75//---------- 76//何らかのタイミングでの処理 77void XXX() 78{ 79 /* ... */ 80 81 if( !TheStack.empty() ) 82 { 83 auto OPs = TheStack.top()->DoSomething(); 84 for( auto &OP : OPs ) //返されてきた操作要求を実施する 85 { 86 std::visit( [&TheStack]( auto &OP ){ OP.Exec(TheStack); }, OP ); 87 } 88 } 89 90 /* ... */ 91}

追記2

「いやいやそうじゃねぇよ,ごちゃごちゃ言ってないでまず最初にスタックから取り出せと」という方向のご指摘,もっともだと思います.

C++

1//何らかのタイミングでの処理 2void XXX() 3{ 4 /* ... */ 5 6 if( !TheStack.empty() ) 7 { 8 //とにかく最初にやるべきことはこれだろ,と. 9 auto TakenOut = std::move( TheStack.top() ); //変な考えは捨てて取り出すべし 10 TheStack.pop(); //(ここでpopもやるかどうかというのは以降の実装の都合次第かもだが) 11 12 //とにかく取り出したなら,そもそもここで変な問題は起きない 13 TakenOut->DoSomething(); 14 15 //--- 16 17 //(必要なら TakenOut をスタックに再度積み直すだとかいう話はあるかもだが,それは今の「問題」とは別の話) 18 if( 何らかの判断 ){ TheStack.push( std::move(TakenOut) ); } 19 } 20 21 /* ... */ 22}
TakaiY👍を押しています

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

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

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

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

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

u2025

2025/09/09 12:31 編集

そもそもスタックの拡張の仕方がおかしいと思うのですが、どうなんでしょうか? スタックのコンテナ(stack)とスタックのアイテム(elem)があって、アイテム側からコンテナを操作したいって不自然ですよね。 (依存が循環参照しているし、責任の所在がelemにあるのかstackそのものにあるのか整理されていない) スタックのアイテムがスタックのコンテナを操作するのではなく、スタックのコンテナがスタックのアイテムを操作するとか、スタックのコンテナの操作を抜き出して、スタックマネージャーみたいなクラスの内部要素にしたらいい気がするのですが。 と、思ったのですが命令そのものをスタックに積みたいって要望だったのですね。後で回答します。
winterboum

2025/09/09 23:43

C++な人では無いから、 かも、なのですが ??? 状態です 「pop した DoSomething を実行する」では駄目なのでしょうか? 「top に DoSomething がある前提」なのだとしたら、DoSomething の最初に「自身をpush」を入れれば良い ではだめ?
fana

2025/09/10 01:31

> 命令そのものをスタックに積みたい 多分そんな感じですね. 要素内で新たな命令を追加したくなったり,いくつかの(現在スタックに積まれている)命令をまとめてキャンセルしたりしたい……みたいな. > スタックのコンテナの操作を抜き出して… スタックの要素が直接その場でスタックを操作するのではなくて,やりたい操作をオブジェクトで表現してそれをどこかに渡す(→その操作は後で適切なタイミングで実施される)みたいな方向性はあると思います.
fana

2025/09/10 01:33

> 「pop した DoSomething を実行する」では駄目なのでしょうか? 今の例だと,要素の外側では,popすべきかどうかは判断できない形になっています. {popすべきか,いくつまとめてpopすべきか}は具体的な要素の DoSomething() メンバ関数の中でしかわからない(何なら動的に定まるかもしれない)という形です. > DoSomething の最初に「自身をpush」 すみません,これに関しては明確なイメージが湧きません. 「自身の延命を図る」という意味合いなのであれば,既存回答がちょうどそういうことになっていると思います.
fana

2025/09/10 01:46

(自分の質問に自分がコメント書いた旨の通知がくるようになったのか……)
dodox86

2025/09/10 02:18 編集

> (自分の質問に自分がコメント書いた旨の通知がくるようになったのか……) これは最近のメンテ以降の挙動のように思いますが、遅れて通知が届いて自分以外の他の方からのコメントかと勘違いしがちで、邪魔でこそあれ有益さを感じません。なのでバグあるいは処理抜けなどに伴う不具合だと思っています。まぁ、他の不具合も含めて直さなければならない義務はないと思いますので、私は積極的に連絡することはしていませんが。 [一部編集済み]
winterboum

2025/09/11 02:36

「( DoSomething() が呼ばれた際には自身がスタックの top だという前提なので)」 「popした時点でその要素自身が破棄されてしまう」 ですが pop した要素(これはなにかの変数に取るとおもうのだが)をpushし直し、変数にとって置いた要素のDoSomething() を実行 では駄目なのですか?
fana

2025/09/11 03:04 編集

> pop した要素(これはなにかの変数に取るとおもうのだが)をpushし直し、変数にとって置いた要素のDoSomething() を実行 これは,スタックのトップ要素の DoSomething() を呼ぶ直前に,トップ要素のコピーをスタックの外側(:なにかの変数)に作れば良い,という話でしょうか. そういう話だとしたら…… 本件の場合,要素は unique_ptr という話になっているので単純にコピーは作れませんが,「Popされても問題無いような(:Popの数合わせのためだけの)ダミー要素を Pushし直しておく」ということは不可能ではない…かな. ただし,常にトップ要素をPopするとは限らないので,外側に退避しておいたものを必要に応じて事後に本当にPushし直す(ダミーが残っていたらそれと入れ替える)必要が生じるかと. --- 「コメントした質問にコメントがついた」と「あなたの質問にコメントがついた」がダブルで来るのもやめてほしいニャー
tamoto

2025/09/11 03:07

C++ のことは全く知りませんが、一般的な Stack のイメージで話すと、 「スタックに積んだ各要素は命令を持っていて、それらは Stack そのものを参照できる」という形だと考えると、 これ自体は普通に考えられる設計だと思いました。 話を複雑にしている要素はただ一つだと思っていて、「DoSomething() が呼ばれた際には自身がスタックの top だという前提」が一番意図が分からないところでした。 その前提をなくせば、話はかなり簡単になるんじゃないでしょうか。
dodox86

2025/09/11 03:27

私だったら「要素自身からそのコンテナ自体を操作する」コードにはしない設計にしただろうと言うのと最近のC++から遠ざかっていたので静観していましたが、tomatoさんのコメントを読んでなるほどと思い、改めて回答の行方が気になりはじめました。 > 「DoSomething() が呼ばれた際には自身がスタックの top だという前提」が一番意図が分からないところでした。 なんか例えば要素自身かコンテナ側で onPopping, onPopped みたいなイベントハンドラー的な明示的なメンバー関数があれば、分らない設計ではないかなと。 質問をかきまわすつもりは無いのでこの辺で。
fana

2025/09/11 04:10

> その前提をなくせば、話はかなり簡単になるんじゃないでしょうか。 前提が無いならば→どう簡単になるのか? …というのが想像できません. 「DoSomething()内から(何も対策を講じずに)Pop()を呼ぶと,Pop()から処理が返ってきた時点ではもうPop()を呼んだオブジェクトが解体されているから困る」 という問題が,前提を無くすならば 「~(略)~ 解体されている 可能性がある から困る」 に変わる(何回目のPopで自身が解体されるかが不明…という,より不確定な話になる)と思うのですが.
tamoto

2025/09/11 04:42

> Pop()から処理が返ってきた時点ではもうPop()を呼んだオブジェクトが解体されているから困る もしかして C++ だと、Stack から要素が Pop されるとき、同時にその要素がメモリ上からも消去されるような話になっているんでしょうか。 Stack を操作したいなら、その Stack はあくまで何らかの要素の「積み上がり方」を表現する構造であるべきで、各命令オブジェクト (要素) のライフタイムと Stack 上の要素の取り出し状況は独立している必要があると考えていたのですが、C++ ではそういうことは難しいんでしょうか? C++ のやり方には詳しくなくて申し訳ありません。
winterboum

2025/09/11 05:22

>もしかして C++ だと、Stack から要素が Pop されるとき、同時にその要素がメモリ上からも消去されるような話になっているんでしょうか。 これはありえないでしょう。そういうstackだったら貯めても使えないわけで、Stackというよりはゴミ箱、というかブラックホール fanaさん 私も tana さんと同じ様に C++の人出はないので、 >これは,スタックのトップ要素の DoSomething() を呼ぶ直前に,トップ要素のコピーをスタックの外側(:なにかの変数)に作れば良い,という話でしょうか. こういう話になるのが ??? なのです。 C++のstackって popすると何が起きるの? が他の言語と違う? アセンブラの時代から stack というのは「後で使う情報を詰め込んで」(push)「使う時に後入れ先出しで取り出す」(pop)わけです。取り出したものは「使う」ために取り出すのだからその情報を何らかの変数から参照できるようになっている(のが私の、多分大方の)理解。その参照の仕方はobject指向言語なら おそらくobject(要素)へのポインタ(の様なもの。rubyだと多分object_idだろうと思う。読んでない) だから、popしたものがそのまま 要素として使えるから「全て static メンバ関数に移して」なんて不用。 DoSomething()実行の前提が その要素がstackのtopにある というのが前提だと、stack topが変わってしまってるのは、まずいから pop したら pushしなおしてから実行する C++の方々 「C++のスタックってちがうんだよねぇ〜」ということでしたらぜひ教えてください
xebme

2025/09/11 06:59 編集

Stackを知っているObserverにeventを通知する。結合度を下げたいですね。 あるいはStackを知る関数を実行するだけ。(C++の関数型機能を前提 )
winterboum

2025/09/11 05:39

chatGPTに聞いてきました。C++ って違うんですね! でもこれはできそう object = stack.top(); object.DoSomething() と感じたのですが、C++ではできない?
fana

2025/09/11 06:38 編集

> もしかして C++ だと、Stack から要素が Pop されるとき、同時にその要素がメモリ上からも消去されるような話になっているんでしょうか。 「C++ では……」というよりも,本件の出発点が > std::stack< std::unique_ptr<IStackElem> >; という話になっているのが元凶です. 例えばこれが //スタックにはポインタ(要素のありか)が積まれているだけなので //Popしようが要素がどうにかなるわけではない(スタックと要素のライフタイムとは無関係) std::stack< IStackElem * > とかであれば,問題ないです. (この場合,要素のライフタイム管理に関しては,どこか別の所でどうにかして頑張るということになる.要は,その「どこか別の場所でどうにかして頑張る」っていうのをやりたくないからこんなことになっている) ※「要素」っていう言葉を使うのが難しくなってきたな…… std::stack< IStackElem * > なら単に「スタックの要素」って言ったら「ポインタ」になっちゃうんだけど, ここでは話の流れ的に「そのポインタが指し示しているオブジェクト」のことを「要素」と書いています.
fana

2025/09/11 06:43

> でもこれはできそう > object = stack.top(); > object.DoSomething() これも,スタックが > std::stack< std::unique_ptr<IStackElem> >; という話ゆえに,簡潔にはいかない,みたいな. そういうことをするならこうなるかな? 的な話が 先のコメント(「2025/09/11 12:04 編集」ってなってるやつ)です.
fana

2025/09/11 07:24 編集

ひょっとしたら誤解されているのかもしれないので述べておきますが, この質問に初心者マークが付いていたり,一文目に「C++初心者です」と書いているのは「なんかおしゃれだから」とかそういうのじゃなくてガチです. ガチ初心者質問です. 当方,普段C++を使っているわけじゃないです.10年くらいまともに触れてなくて最近「なにこれイミワカンネー」ってなってる人です.勉強中です. (「なんか聞くところによると C++11 とかいうのが素敵らしいぞ」みたいな話を聞いたり聞かなかったりしていたあたりの時期で離れました.) なので例えば 「まずもって std::stack< std::unique_ptr<IStackElem> >; とかいう出発点がもうダメだろ常考」みたいなところからの話とかもあり得るのかもしれません. (やっぱり 意見交換 の方が良かったのかも?)
cametan

2025/09/11 15:49

全然C++は良く分かってないんだけど、そもそもstackをクラスで実装せなアカンのか?ってのが気になった。 C++だとSTLに既にstackが積まれていて、 https://cpprefjp.github.io/reference/stack.html つまり、敢えてやってる、ってケースになってるかもしんないけど、一応「車輪の再発明」になってる。 また、DoSomethingも、メソッドっつーかメンバ関数?にする必要があるのか、ってのが気になった。 大域変数的にスタックがあれば、それを外部から操作する関数は書けるわけで、それをスタックに積む事は不可能ではないんじゃ、とか思った。 多分その方がシンプルになるのでは、と。 ハズしてたらごめん。
winterboum

2025/09/12 08:26

済みません C++ わかって居ないのに口を挟んで雑音になってしまったのではと、、、 興味は有るのでここ読み続けますが、とりあえずは RO してます
fana

2025/09/13 03:21

> そもそもstackをクラスで実装せなアカンのか? 今回の場合「stack自体」は自前で実装はしていません.リンクで示していただいた std::stack を用いています. > それを外部から操作する関数は書けるわけで、それをスタックに積む事は不可能ではないんじゃ 他の方からの回答のほうにも同じことをコメントしていますが, 「関数」でやれていないのは,純粋に私のC++技量の問題です.
fana

2025/09/13 03:26

> C++ わかって居ないのに口を挟んで雑音になってしまったのではと、、、 そんなことはありません. 質問には C++な部分の要素と,「そもそもスタック使う場合ってのはそうじゃないのでは」みたいな要素 が含まれていると思われ,後者側に関する ご意見/ご指摘 をいただくこともありがたいのです. それを私なりに解釈したらこんな感じになっていますがどうですかね的なのを質問に追記していっております. (本件で用いている言語がC++なので,そこはどうしてもC++での記述になってしまいますが)
guest

回答3

0

c++

1 std::unique_ptr<IStackElem> self; 2 if (m_nPop > 0) { 3 self = std::move(m_Owner.top()); 4 for (int i = 0; i < m_nPop; ++i) m_Owner.pop(); 5 } 6 m_Owner.push(std::move(m_ToBePushed));

のようなことが可能だと思います。staticメンバのアプローチより少しコードがシンプルかも、という程度ですが。


スタック要素のメソッドでスタックを操作するのが危なっかしい設計に感じますが、そのへんは全体を見てみないと妥当性はわからないですね。

投稿2025/09/09 07:38

編集2025/09/09 23:39
int32_t

総合スコア22002

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

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

fana

2025/09/09 09:30

なるほど,ローカルな unique_ptr に所有権を移すことで自身が破壊されるタイミングを必要なだけ遅らせるわけですね.
SaitoAtsushi

2025/09/09 14:15

もしも m_nPop がゼロの可能性がある仕様の場合はムーブしたらまずいので場合分けが要りますね。
int32_t

2025/09/09 23:36

> m_nPop がゼロの可能性がある たしかに。回答を更新しておきます。
fana

2025/09/11 04:19 編集

「pushだけやる」と「pop して push する」とに分岐させる必要があるかと. (push も self 生存中にやらないとまずい) ↓ あ,読み間違いか.self は if の外にあるんですね.
guest

0

std::stack<std::function<int()>>

逆ポーランド記法3 5 *を実行する例を考えました。スタックにintを返す関数を積むことにします。使っているのは以下の関数機能です。

  • ラムダ式
  • ラムダ式で使う変数(スタック)のキャプチャー

cpp

1#include <iostream> 2#include <functional> 3#include <stack> 4 5int main() { 6 std::stack<std::function<int()>> s; 7 8 s.push([](){return 3;}); 9 s.push([](){return 5;}); 10 s.push( 11 [&s]() mutable { 12 auto x = s.top()(); 13 s.pop(); 14 auto y = s.top()(); 15 s.pop(); 16 s.push([x,y](){return x*y;}); 17 return x*y; 18 }); 19 20 std::function<int()> f = s.top(); 21 s.pop(); 22 f(); // claculation 23 std::cout << s.top()() << std::endl; 24 return 0; 25}

ちなみにC++はど素人です。さっき勉強しました。スタックやリストに関数を格納するのは他の言語ですでに経験済み。

投稿2025/09/12 14:16

xebme

総合スコア1113

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

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

xebme

2025/09/12 23:12 編集

「高階関数」というキーワードを忘れていました。 関数からスタックを操作するので副作用があります。もっとよい実装を考えてみてください。
fana

2025/09/13 03:36

(他の方とのやりとりの具合からもわかるかと思いますが)当方の読解力が低めであるため, まずは回答内容の確認からさせていただきたく. 「std::function をスタックに積む,という方法がある」というのが趣旨という理解で合っていますでしょうか. そうである場合…… 私のC++力量だと 「質問内の struct Problem みたいなのを std::function を用いた実装にすること」のところの難易度で躓いてしまう感じです.
guest

0

追記を見ましたが、Problemの実装は何でもいいんでしょうか。。。?
最初のProblemをみてアンチパターンの見本市みたいなコードだなぁ。と言おうと思ってコードを作っていたらこのアンチパターンの見本市のせいで、うまくいかないパターンがあるなぁ。ということに気が付いたので書き直しました。

私もC++は読めない&書けないのでChatGPTに書いてもらっています。(コメントが白熱していることもさることながら、段々回答のタイミングを見失っていく。。。)

StackManager, StackElem, StackOperatorを追加しました。
StackElemはStackOperatorを受け取ったり返したりすることに専念しつつ、Stackに積まれる役割を果たします。
StackManagerはStackElemから要素を取り出したり、取り出した要素を実行したりする役割を担います。
多分回答しても、このパターンが。。。!的なのが出てくるのでしょうが、それについても対応できそうであればします。
StackOperatorを使いまわしたいとかであれば、StackElemのgetOperatorが呼ばれるたびにstd::unique_ptr<StackOperato>のインスタンスを生成して返すアプローチの方針を考えていますが、結局依存関係が循環していて。。。
そのパターンを考慮すべきかわかりません。

ちなみに、スタックに要素がないのにpop命令が入ることはあるでしょうが、考慮してません。

以下GPTに書いてもらったコード

cpp

1 2#include <iostream> 3#include <memory> 4#include <stack> 5 6// 前方宣言 7class StackManager; 8class StackOperator; 9 10// ========== (2) StackElem ========== 11class StackElem { 12public: 13 explicit StackElem(std::unique_ptr<StackOperator> op) 14 : op_(std::move(op)) {} 15 16 // StackOperator を取り出す(moveで返す) 17 std::unique_ptr<StackOperator> getOperator() { 18 return std::move(op_); 19 } 20 21private: 22 std::unique_ptr<StackOperator> op_; 23}; 24 25// ========== (3) StackOperator ========== 26class StackOperator { 27public: 28 virtual ~StackOperator() = default; 29 30 // Managerからスタックを渡されて処理を行う 31 virtual void execute() = 0; 32}; 33 34// サンプル実装: スタックのサイズを表示する 35class PrintSizeOperator : public StackOperator { 36public: 37 // stack参照と pop/push 用の情報をコンストラクタで受け取る 38 PrintSizeOperator( 39 std::stack<std::unique_ptr<StackElem>>& stack, 40 int nPop = 0, 41 std::unique_ptr<StackElem> toBePushed = nullptr 42 ) 43 : stack_(stack), nPop_(nPop), toBePushed_(std::move(toBePushed)) 44 {} 45 46 void execute() { 47 // 指定回数だけ pop 48 for (int i = 0; i < nPop_ && !stack_.empty(); ++i) { 49 stack_.pop(); 50 } 51 52 // push が指定されていればスタックに追加 53 if (toBePushed_) { 54 stack_.push(std::move(toBePushed_)); 55 } 56 57 // スタックサイズを表示 58 std::cout << "Stack size after execute: " << stack_.size() << std::endl; 59 } 60 61}; 62 63// ========== (1) StackManager ========== 64class StackManager { 65public: 66 67 // stack の参照を返す 68 std::stack<std::unique_ptr<StackElem>>& getStack() { 69 return stack_; 70 } 71 72 // StackElem を積む 73 void push(std::unique_ptr<StackElem> elem) { 74 stack_.push(std::move(elem)); 75 } 76 77 // StackElem のオペレーターを実行する 78 void executeTop() { 79 if (stack_.empty()) return; 80 81 // topから取り出す 82 // :: getOperatorがmoveしてくるので、topの参照を残せない(?) 83 // :: 必要であればgetOperatorが新しいインスタンスを返すようにする 84 auto elem = std::move(stack_.top()); 85 stack_.pop(); 86 87 // operator を取り出して実行 88 auto op = elem->getOperator(); 89 if (op) { 90 op->execute(stack_); 91 } 92 } 93 94private: 95 std::stack<std::unique_ptr<StackElem>> stack_; 96}; 97 98// ========== 動作確認 ========== 99int main() { 100 StackManager manager; 101 102 // stack 参照を渡して PrintSizeOperator を作成 103 auto op = std::make_unique<PrintSizeOperator>(manager.getStack()); 104 105 // StackElem にラップして push 106 manager.push(std::make_unique<StackElem>(std::move(op))); 107 108 // 実行(現在の stack サイズを出力) 109 manager.executeTop(); // → "Stack size: 0" 110 111 return 0; 112} 113

投稿2025/09/11 11:13

u2025

総合スコア88

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

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

fana

2025/09/12 02:34

以下のような話と読みました. * 「スタックに積まれる物」と「やりたいこと」を明確に分ける. * 「スタックに積まれている物に何かをやらせる」のではなくて,スタックに積まれている物から「やりたいこと」を取り出して用いる.
u2025

2025/09/12 08:26 編集

そうですね この例では取り出してますが、実際には、常にインスタンスを返す関数としてもいいという話です。 そもそもとして関数のポインタ(?)を返せばいいと思ったのですがC++ではオブジェクトを返すのが普通みたいなので。 そういう趣旨だと思ったのですが、もしかしてそういうことでは無い...? 何か問題がある感じでしょうか。 > 「スタックに積まれる物」と「やりたいこと」を明確に分ける. そもそも、スタックに積まれてるものがやりたいことを持っているからやりたいことが破棄されてしまうというのが本件の趣旨なのでは...? だから私の回答の意図そのものではないですが包含されていると思います。。。 特に返信もなく、質問編集したということは何か困っているという訳では無い? まあ実装出来たらなんでもいいんじゃないでしょうか...? やり方だけならプログラムはどんなやり方もよしとされるでしょうし、スタックを使う理由やどのようなパラダイムを採用するのか、そういった意図を明確にしないとクリティカルな回答は続いてこないと思いますよ。staticで持つとかは直感的に捉えるとやりたいことそのものが矛盾しているようにも見えるし(だからこそメモリ管理をしたくてstaticを使いたくないということかと捉えたのですがそうでもないみたいですし)、そもそも何らかのクラス定義ややり方があってその実装に耐えられる設計にしたいとかいう訳でもないみたいですし
fana

2025/09/13 03:18 編集

> そもそも、スタックに積まれてるものがやりたいことを持っているからやりたいことが破棄されてしまうというのが本件の趣旨なのでは...? 全くその通りです. 先に書いたコメントは,「であれば分ければいい」という主旨を回答いただいているのですよね? という確認です. そういう,こちらに欠けている視点みたいなのを指摘いただくことは大変助かります. 関数ポインタでやれればそっちの方が良いだろうと私も思うのですが,そこは純粋に「私のC++技量面の問題でうまくやれない」がためにオブジェクトになってしまっているというのが現状です. 質問編集(追記2)は「質問へのコメント」の方でいただいている意見に関して 「えっとつまりそれはこういう話というですよね」みたいなのを述べているものです.
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.30%

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

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

質問する

関連した質問