C++相談室 part156
■ このスレッドは過去ログ倉庫に格納されています
前スレの>>989
> 可変個の参照の組 (vectorでいい) を関数 hoge に渡したいときって、hoge が vector< reference_wrapper<T> > を取るようにして
> hoge({ref(A), ref(B), ref(C)})
> みたいに呼ぶか、可変引数テンプレートを使って hoge の中でパースするかっていうのが普通のやり方かな?
なんですが、もしかして reference_wrapper って構築時に左辺値を渡したらそれの参照を保持してくれる?
だとしたら
hoge({A, B, C})
と呼べるしかなりスッキリしますね
hoge 内でいちいち get() しないといけないのはダルいですが…… 継承や委譲との付き合い方がよく分からなくなった
あるクラスの機能を全部持っていてほしいが is-a 関係がないとかで継承関係にはないように思える場合ってどうすんの
例えば一辺の長さを表す変数やはみ出し判定メソッドを持つ「グリッド座標クラス」があったとして、今「オセロを解くクラス」を作りたいとき
オセロを解くこと is a グリッド座標では全くないし継承するのは頓珍漢に思える
一方で「オセロを解くクラス」がグリッド座標のインスタンスを委譲として持っていたとして、盤の大きさとかはみ出し判定メソッドにわざわざ「グリッド座標クラス」のインスタンスを通してアクセスするのも果たして正しいだろうか
例えば盤面 a と盤面 b を同時に持ったりもするだろうが、a.size() とか b.size() は同じものを表すのでこのようにアクセスするのは可読性を損なう
だから「オセロを解くクラス」は盤面の大きさとかはみ出し判定メソッドを自分のメンバとして持っていてほしいが、継承するべきようにも思えない 複数の盤面持つんならメンバで持つ一択だと思うけど
「解くクラス」に外部からはみ出し判定アクセスするのもおかしな話だけど
描画の都合とかで外部からアクセスするのが欠かせないなら、インデックス受け取って盤面の参照返すメンバ関数でも用意すればいい あ、あるいは解くクラスが単に外部の盤面を参照する形でもいいかもね >>7
> 複数の盤面持つんならメンバで持つ一択だと思うけど
「オセロを解く」といった時点で盤の大きさ等決まるんだから、自分のメンバとして基本的な機能持っててほしいと思うんですが、偏った考え方ですかね
> 「解くクラス」に外部からはみ出し判定アクセスするのもおかしな話だけど
そう思います >>8
それはインスタンスとして盤面を持つという意味ではなくですか? フロント部分とオセロを解くソルバー部分で持ち方変えるってのが普通 そう、内包しようとしない方が自然かもしれない
(読んだ範囲で勝手に考えてるだけだけど)
例えばその解くクラスってのがAI的に自動で解くクラスなら、じゃあその対戦相手に同じ(だがインスタンスは別)AIや
プレイヤーが参戦したらどうなるんだ?と考えると
盤面は独立してるほうが自然な気はする(個人の見解です >>6
オセロを解くアルゴリズムとオセロをプレイするモデル次第なのでなんとも。
まあ、オセロを解くんだったら多数のオセロ盤を持つだろうから、グリッドクラスを派生させてオセロ盤クラスを作るだろうな。
人間がオセロをプレイするのを素直にモデル化してもいいけど、ゲーム機のオセロゲームをプレイするようにモデル化したほうが、オセロを解く側のミスや負担が減る。 >>11
フロント部分って今の場合だと「オセロクラス」ってことですか?
つまり「オセロクラス」と「オセロを解くクラス」が、どちらがどちらを含むではなく、やり取りしあって動くべきだということですか? >>13
派生って継承ってことですか?
つまり必ずしも is-a を満たさなくても機能を全部引き継ぐなら継承は是 (あるいはそうモデル化するのが良い) ということでしょうか かんたんだろ
色々判定してくれるオセロ妖精すなわち神を作ればいい >is-a を満たさなくても機能を全部引き継ぐなら
どちらも自分で書いてるしグリッドクラスも継承を想定して作れるんだからそれでいいとおも
他人の書いた継承を想定してなさそうなのはやめといた方がいいけどね
てかis-a,has-aはあくまで迷ったときの指針に過ぎんし自分で継承が良さそうと感じたならそれで正解だよ >>15
この場合、is-aは「グリッドとして同一視できる」を意味するから、オセロ盤をグリッドとして扱うことができるのなら継承もあり。
ただc++の継承は、継承元を総称として扱うという強い意味(継承元と派生クラス全体の強い結びつき)もあるのに注意。
オセロ盤とかその他のゲーム盤とかのクラスを(グリッドとして)使うということでもなければ、継承しないほうが設計の自由度を保てる。 >>18
> ただc++の継承は、継承元を総称として扱うという強い意味(継承元と派生クラス全体の強い結びつき)もあるのに注意。
まさに、そういった意味論的なところで悩んでいます
まあ実際にオセロソルバを作ろうとしているわけではないのですが、同じような局面に何度も出くわして、継承も委譲もどうも適切でないように思えて最終的に「グリッドクラス」の中身をコピペして「オセロソルバクラス」を作るようなこともしばしばしてしまいます そんなん言い出したらメタプログラミングで取り入れるメンバを選択するための継承なんか全部アウトだぞ
STL内部でやってるようなのも全部
ていうか
>まあ実際にオセロソルバを作ろうとしているわけではないのですが
>まあ実際にオセロソルバを作ろうとしているわけではないのですが
>まあ実際にオセロソルバを作ろうとしているわけではないのですが 継承でできることを別の手段でもできるって言ってるだけでは説得力がない
そんなん言い出したらC++でできることをCで擬似コード書けるしな
特にコード量が増える「別の手段」を推奨するには
格段に強い理由がないと誰も動かせない cpprefjp によると std::swap の実装って
void swap(T& a, T& b){
auto tmp = move(a);
a = move(b);
b = move(tmp);
}
みたいな感じですよね?
よくされる「move された後のオブジェクトはどうなってる保証もないのでもう触るべきでない」みたいな説明に照らせば、上のコードは move した後のオブジェクトを再利用してるけど大丈夫なのかなって思ってしまいます
大丈夫なんでしょうか?
また、参照をmoveしたら当然参照元のオブジェクトが「どうなってる保証もない」と考えるべきなんでしょうか? もう一個質問させてください
引数に参照をとる関数に一時オブジェクトを渡したいこともあるんですが、この場合は const参照か右辺値をとる関数としてオーバーロードするしかないですよね? >MoveConstructibleかつMoveAssignable
こうなってるからヨシ! >>22
move後の変数は、読み取った場合の値はどうなってるか保証できないが、
値を書き込むことは正常に出来る事が保証されていると聞いた。
破棄することや、初期化することも問題ない。 >>25
moveコンストラクタは呼ばれないの?
呼ばれるのなら保証するのは自分自身なのでは? >>27
moveコンストラクタは、変数定義を
TYPE x = y;
の形式で書いた場合に y が右辺値の場合に呼び出されるだけ。
move代入は、
x = y;
の形式で書いた場合に y が右辺値の場合に呼び出されるだけ。
後者の場合、xがmove後であるかどうかは関係ない。
変数xの中身をmove後のxへの書き込み、xの破棄、x.init()などとしての
再初期化が「保証される」というのは、保証されるようにSTLライブラリなどが
作られているという意味で、コンパイラが保証するというわけではない。 T が値なら終了して、vector<T> なら再帰的に自分を呼ぶ関数を作りたいのですが、T が vector (あるいはコンテナ) かどうかで分岐する処理ってどうやって書くのが良いですか
あと近い話題で、+ 演算子をオーバーロードして vector + vector が結合された vector を返すようにするのってナシですかね?(string のように)
要素同士の和をとるのと紛らわしいですか?
cat(vector, vector) とかの方が良いかな 固定長文字列クラスってないんですかね
template<int N> void hoge(string s){
……
assert(N == s.size());
array<int, N> a;
……
}
なる関数を呼び出すときにテンプレートパラメータを省略したいんです
s が固定長なら s から N を推論してくれますよね あるいは hoge の引数を costexpr に限る方法なんかがあれば、N は必ずコンパイル時に推論できるようになるの思うので、もしそういうものがあればそれでも大丈夫です もしかして、C++11に対応したSTLで、BSD/MIT 系ライセンスのものは存在してませんか?
・apache-stlは、2008年くらいで開発が終了しているようです。
・msvc用のstlはapacheライセンスですが、vcruntime.hが存在しないと
エラーになるようです。 >>30-31
これマジキボンヌなので何卒よろしくお願いします >>30
よくわからんが何で長さの相違を型の相違にしたいんだ?
文字列リテラルならこういう手があるけど
template <int N> void hoge(const char(&s)[N])
{
} >>34
整数のテンプレート引数を使って、std::stringを継承した文字列クラステンプレートを作ればなんとかなりそう。試してないけど。
std::stringの継承はけっこうクセがあった気がするから、自分で調べてね。 >>30
>テンプレートパラメータを省略したいんです
の理由がただお遊びレベルのような気がしてスルーしてたけど
固定長の文字列クラスは標準には無い、欲しけりゃ探すか自分で作れ、それか>>35 コンパイル時プログラミングのための機能を満載したライブラリ Sprout がある。
constexpr 対応した文字列クラスもある。
http://boleros.hateblo.jp/entry/20110926/1318115291
ボレロ村上が亡くなってもう一年以上たったんだなぁ……。
Sprout のメンテナンスを引き継ぐ大きな組織とかないのかね?
いっそ Boost に編入するとかでもよいような気がするが。 >>35
> 何で長さの相違を型の相違にしたいんだ?
すみません。どういう意味でしょうか
やりたいことは、関数に渡した定文字列の長さをコンパイル時定数として扱うことです
> 文字列リテラルならこういう手があるけど
ありがとうございます
これが現在やりたいことに最も近いです
2つの文字列リテラルの共通部分列の長さをコンパイル時定数として扱うみたいなこともできそうで、ワクワクしています
>>36-37
ありがとうございます
自作は想像だにしない失敗に繋がりそうなのでやめておきます
固定長文字列とか引数を constexpr に限るとかすごい便利な機能に思えるんですが、今後標準に入ることはあるんでしょうか
それとも僕の不理解で便利に見えるだけなんでしょうか 固定長文字列ってCOBOL以外にあるの?
FORTRANにも無いしCにも無い
現行言語には存在すらしない
昔から特殊技術で需要が少ない malloc使わなかったらcも固定長でしょ
あとsql >>39
文字列長ごとにメソッドが別になってプログラムサイズの肥大化に繋がる。
スタックに文字列を全部乗っけて(若干の)スピードアップを狙うこともあるけど、そのときでも「〇〇文字以下」みたいにサイズ制限するくらい。Delphiの文字列がそうだったと思う。 >>40
FORTRANにもあるしPascalにもあるぞ
需要じゃなくてお前の知識が少ないだけだろw clangで、#includeや#include_nextした全てのインクルードファイルの
パスの一覧をテキストファイルに出力することは出来ますか? >>44
-MD オプションかな。 これは gcc と clang で共通。 すみません
>>5の方法で可変個の参照の組を関数に渡そうとして行き詰まりました
hoge の定義を
template<class T> void hoge(vector< reference_wrapper<T> >);
として、hoge({a, b, c}) と呼んだら T が推論できないとエラーが出ました a, b, c がfoo_t型だとして
単に型foo_tを明示して hoge<foo_t>({ a, b, c}) と呼んだら良いんジャネーノ;;; 質問ですがvirtualでないクラスFoo(つまりdynamic_cast適用不能)のオブジェクトをthrowしたら
catchできます? >>47
ありがとうございます
ただ、なぜに推論できないのでしょうか
template<class T> void hoge(vector< reference_wrapper<T> >);
と
template<class T> void hoge(vector<T>);
が両方あってどっちか判断できないって話なら分かるんですが reference_wrapper<T> をテンプレート型として認識できないからでしょ >>50
ありがとうございます
コンテナの入れ子の中に T があっても推論してくれる例を知っていたので、いつでもできるのだと誤解していました { 1, 2, 3 }というだけではstd::vector<int>なのかstd::vector<double>なのか(ひょっとしたらstd::vector<long>とかかも??)
わからないからという理由
自動型変換結果とのマッチングは追求しだすときりが無いので規格でどの範囲でやるか制限がかかっているたはず >>48
C++はintだって投げられるし捕まえられるぞ >>53
投げたブツの型の情報がどうやってcatchする場所に伝わっているのかキボン Emacs で LSP 使わず開発してる人いる?
おもしろ設定とか参考になるサイトあったら教えてほしい
flycheck しか入れてない >>54
関数呼び出しと一緒やぞ
catch節に書いた仮引数でオーバーロードしてると思えばいい >>57
ありえん
1つのthrowに対し、呼び出し元のcatchで呼び出すべき(オーバーロードされた)関数シンボルを一意に列挙できるなら
アンワインド時にオーバーロードのしくみで例外ハンドラの解決を行えるかもしれないが、これは論理的に成り立たない
なぜなら、例えばintを例外をスローする関数foo()がbar()とbaz()から呼ばれているとして、
bar()にcatch (int)が書かれているのにbaz()に描かれていないとした場合、
bar()経由の呼び出しにおいては(オーバーロードされた)関数シンボルのリストに func(int)が含まれるのに対し、
baz()経由の呼び出しにおいては(オーバーロードされた)関数シンボルのリストに func(int)が含まれない、
となって一意にならず、throwした瞬間にどっちなのか解決不能
やっぱcatch側が(何が来るかわからない状態で)待ち受けているとしか考えられない ちょっち補足すると、
bar()でtry { foo(); } catch (int ex) { .... } したときに handler(int)を例外ハンドラのリストに動的に登録し、
baz()ではtry { foo(); } catch (int ex) { .... } が無いからhandler(int)を例外ハンドラのリストに動的に登録しないようにすれば、
foo()でthrowしたときに最新の例外ハンドラのリストを参照することによって解決はギリ可能だが、
スタックとは別に例外ハンドラのリストに動的に管理するみたいなことをしているのかとカナーリ疑問に、
(例外がfall throughする関数の中にはゼロコストの奴も含まれるので、スタック上で細工して例外ハンドラのリストを更新管理することは不可能 exception_ptrの主な用途は、バックグランドスレッドからメインスレッドに、例外オブジェクトを持ち運ぶ、というものである。標準ライブラリにおいては、promiseとfutureの実装で使用される。
と書いてあってスレッド間をまたぐ用(promiseしたことを実行中に起きた例外を、promise完了を待っているやつに伝える)とき限定に見える
単純にcalleeからcaller側に飛ばす通常の例外ではどうなのや??
intやdoubleやconst char*を型情報とともに収容可能なexception_ptrみたいな構造体が絡んでいるのだろうとは思いまするが >>61
スレッドは関係ないぞ
同一スレッドでも監視ブロックを抜けrた後でrethrow_exceptionできるし >>63
言わんとすることはわかりまするが
(そもそもpromise/futureのしくみも論理的には単なる継続なので単一スレッドでの実装もあり得るという意味でスレッドは関係無い
しかしrethrow_exception(ex)はrethrow_exception(ex)であってthrow exとは別の文やん??
本当に単純にcalleeからcaller側に飛ばす通常の例外も同じしくみなのかどうか、、、 スマン>>58、>>59は撤回
やっぱ関数のオーバーロードで呼び出すべきハンドラを解決している可能性がありえまつね
というのはスタックにはreturn addressが積まれているので、throw intするfoo()は、
throwする瞬間に自分がbar()経由で呼ばれたのかbaz()経由なのか判定可能
あとはreturn addressに対して呼び出すべきハンドラ(のシンボルのリスト)を与えるテーブルがあれば、
関数のオーバーロードで呼び出すべきハンドラを解決できる
極力実行コストを低くする手となるとこれかなあ……
長々とスマンカッタ、継続調査しまつ >>56
>Emacs で LSP 使わず開発
その発想がナンセンス >>66
lsp-mode インストールしてファイル開いてみたら「これはなんのプロジェクトにも属してないファイルだ」とか言って何もしてくれなかったもん
テキストエディタだぞ
単一ファイルのソースも普通に編集できるべきだ インストールさせるという選択肢である以上は
emacs側はそれをやるかどうかの選択をユーザに委ねていることになる
なのでNoという選択もありうる、とemacs側は考えている
選択肢が無く強制なら選択をさせずインストール現象もまた起こらない
インストールさせるということはYesかNoかをユーザが選ぶことができる仕組みになっているからだ
YesでもNoでもなく選択出来ないならインストールさせない仕組みになっているハズだ
ここでインストールできるということはユーザのYes/Noの意思が尊重されることに他ならない
選ぶ・選ばない・強制、などにおいて、選ぶことが出来る事象については何を選択してもいいし、emacs側もそれを容認している >>64
よくわからんが、こういうこと?
void caller()
{
exception_ptr ex;
ex = callee();
try
{
rethrow_exception(ex);
}
catch(double err)
{
cout << "What the hell does that mean?\n";
}
}
exception_ptr callee()
{
try
{
throw 0.1;
}
catch(...)
{
return current_exception();
}
} クラスAがクラスBのインスタンスをメンバに持つとき (移譲っていうんだっけ?)、Bのコンストラクタってどーやって呼ぶんよ C++20 からは std::string は constexpr 対応になっとるな。
実装が追いついとるかどうか知らんけど。 constexpr引数ってコンセプトとか使っても無理なん なぁ…何気に…普通に…何の気なしに…以下の様なコードを書く…
textView1->get_source_buffer()->place_cursor(
textView1->get_source_buffer()->get_iter_at_line(minusList.front() - 1));
なんだけどさ…このplace_cursorの引数はiteratorなんだが…この一時的に作った無名の変数の寿命って…
どうなってるの?…ちなみに…place_cursorは&で受ける…やばいのかな? place_cursorを実行している段階では…生きている…そうだ…次のステップに移行すると…消える…らしい
ほんまかいなと思いますが…一応…信じておきます… >>76
一時的なオブジェクトの寿命が完全式の終わりであることは保証されている。
https://timsong-cpp.github.io/cppwp/n3337/class.temporary#3
参照が一時オブジェクトに束縛されているときは更に寿命が延長されることもある。 勝手に消えるのは確かだが
いつ消えるかは確定出来ないんじゃないの?
次の行の実行中かも知れないし
もう少し生きてるかも知れない
関数抜けたら消えるだろうけど その、意味があるとかないとかを、どうやって判断するかと聞いてるんじゃないかね 記述時に事細かに寿命を制御・確定できるような時制プログラミングを作ればいい
C++の場合だと新しい原理を作ればいい > 参照が一時オブジェクトに束縛されている
一時オブジェクトが参照に束縛されている、だろ
ここではconstでない左辺値参照は含まない >>83
> 一時オブジェクトが参照に束縛されている、だろ
いいえ。 束縛されるのは参照で束縛するのはオブジェクトです。 >>84
規格票での言葉遣いはこうなっているぞ
CA &&r = A{}; // OK, reference binds to temporary array object いや、おかしいだろ
rand(); //ここで消えてしまう一時オブジェクトを
auto&& a = rand(); //引き止める(束縛する主体が参照で
客体が一時オブジェクトだぞ >>85
このへんのセクション内部ではどちらの表現もある。
https://timsong-cpp.github.io/cppwp/n3337/over.ics.ref
"自由変数と束縛変数"
https://ja.wikipedia.org/wiki/%E8%87%AA%E7%94%B1%E5%A4%89%E6%95%B0%E3%81%A8%E6%9D%9F%E7%B8%9B%E5%A4%89%E6%95%B0
このへんの数学的用語では束縛されているのは変数で、 ML 系とか Scheme とかでも一貫してこの用法に従ってるんだけど、
めんどいから私は「××との間に束縛を持つ」みたいな言い回しでどちらが主語なのか曖昧にしたりしてる。
今回の場合は >>79 で示した通りオブジェクトの寿命に関する項目での表現では束縛されているのは参照だから
それにならっただけでどっちがどっちでもまあ重要じゃないよ。 >>89
1つめのリンクでは
結合する主体は参照を宣言するコンテキストだね
結合される客体がオブジェクトである点は変わってない
数学や他言語がどうたらには付き合ってやんね
ちゃんとC++の話のなかで例えるなら
実体定義と外部宣言はどちらが使われる側と使う側なのか
のような話だろ そことは直接関係ないけど、cppreferenceの英語版って、英語として文法的におかしい
ことが有る気がする。主語が無くていきなり三人称単数現在のsが付いた動詞で
始まっている文章も、見出しの直後ならダメではないが、見出しの直後以外の
if節の後にあったりして、しかもそれがif節の中に繋がっていなくて、本来なら
主語を省略できない場合とか。
Microsoftの英語版はすらすら読めるけどcppreferenceの英語版は読めない。 int 配列の参照って
int (&a)[N]
であって
(int&) a[N]
じゃないよね?
後者は「参照の配列」を意味するようで変なのは分かるんだが、前者は前者でどこに&ついてんねんって感じなんだが >>91
てにをはがおかしいのは日本国憲法に比べればかなりマシ。がまんしろ >>92
>int (&a)[N]
それは、数学記号の様に読み取ることが出来る。
()は優先順位を変えているだけ。
優先順位に従って書いてみると、
a --> & --> [N] --> int
となる。これにより、
「a は、参照で、それは、[N] を指し、その要素は int である」
と読めて、もう少し自然な言語に直すと
「a は、参照で、それは、N要素の配列を指し、その配列の要素は int である」
となり、さらにわかり易くすると、
「a は、要素が int の配列を指している参照である」
となる。このように数学の式変形の様な工程を経ているだけの、単なる計算の様な
ものに過ぎない。 >>94
[補足]
a --> & --> [N] --> int
を英語で読んでみると、
「a is & to [N] to int」
となる。& を「reference」、[N]を「N要素のarray」に置き換えてみると、
「a is reference to "N要素の配列" to int」
となる。日本語に直せば、
「aは、int 型の N要素の配列への参照」
となる。 それはたとえば
int (a&)[N]
などを禁止する理由になってるだろうか >>39の
> 2つの文字列リテラルの共通部分列の長さをコンパイル時定数として扱う
をやろうとして苦戦しています
再帰 constexpr 関数で計算しようと思ったんですが、引数として受け取った文字列リテラルの要素って定数と見なせないですよね?
そもそも無理か、勉強次第でできるかだけでも教えていただけたらありがたいです >>96
(a&)
の部分は、()は優先順位を変えているだけなので、コンパイラは
a&
という部分式を認識しようとする。しかし、a& という部分式は
C++では文法的に存在して無いのでエラーとなる。
単にそれだけの事。 >>99
[補足]
C++コンパイラが
int x[N]; // (1)
というテキストをパースする際、コンパイラ内部では、
x --> [N] --> int
という順序の演算が並んでいると理解されている。そして、
int (&a)[N] // (2)
というテキストをパースする時、優先順位括弧があるために、
(1)において、x を &a に置き換えたようにコンパイラは理解するなので、
数学の式変形での「代入」のように考えて、
&a --> [N] --> int // (3)
のようになる。そしてさらに、&a は、a --> & のように式変形されるので、
(3) に安全のために優先順位の()を付けてそれを「代入」すると、
(a --> &) --> [N] --> int // (4)
となる。しかし、この場合、優先順位の括弧ははずせるので、
a --> & --> [N] --> int // (5)
となる。ここで、もとのテキストが、
int (a&)[N] // (6)
だったならば、x が a& に相当することになるが、a& という部分式はC++には
存在しないので、そこでエラーになる。 >>100
[さらに補足]
C++では、「宣言文」と地の文における「式」とは別扱いになっている。
なので、式における &a と宣言文における &a では、& の意味が異なる。
今回の場合は「宣言文」。
宣言文における &a は、a が参照型であることを意味する。
式における &a は、a のアドレスを取得する演算子である。
> x --> [N] --> int
> という順序の演算が並んでいると理解されている。
において「演算」と書いたのは、適切な言葉が見つからなかったため。
演算といっても、今回は宣言文ので、式における演算子とは意味が違う。
式に置いて x[y] は、常に *(x+y) と等価な演算子であるが、
宣言置いて x[N] は、演算子ではなく、x が N要素の配列型であることを
意味しているだけである。
また、最後の xxx --> int も演算子ではなく、型指定部と呼ばれ、
xxx の部分が int 型であるということを意味しているだけである。
しかし、コンパイラ内部では、
x --> [N] --> int
のように意味解釈がされていることだけは確かである。 ■ このスレッドは過去ログ倉庫に格納されています