C++相談室 part132
レス数が900を超えています。1000を超えると表示できなくなるよ。
0001デフォルトの名無しさん 転載ダメ (ワッチョイ faeb-wbjw)
垢版 |
2017/10/10(火) 00:11:34.01ID:nc/5PI4P0
次スレを立てる時は本文の1行目に以下を追加して下さい
!extend:on:vvvvv:1000:512

C++に関する質問やら話題やらはこちらへどうぞ。
ただし質問の前にはFAQに一通り目を通してください。
IDE (VC++など)などの使い方の質問はその開発環境のスレにお願いします。

前スレ
C++相談室 part131
http://mevius.2ch.net/test/read.cgi/tech/1501295308/

このスレもよろしくね。
【初心者歓迎】C/C++室 Ver.101【環境依存OK】
http://mevius.2ch.net/test/read.cgi/tech/1500329247/

■長いソースを貼るときはここへ。■
 http://codepad.org/
 https://ideone.com/

[C++ FAQ]
https://isocpp.org/wiki/faq/
http://www.bohyoh.com/CandCPP/FAQ/ (日本語)
VIPQ2_EXTDAT: default:vvvvv:1000:512:----: EXT was configured
0801デフォルトの名無しさん (ワッチョイ 02bd-VFQ0)
垢版 |
2017/11/19(日) 06:25:20.22ID:p3uF8GIb0
プロセス屋さんががんばってCPUを3年ごとに倍早くしてくれるのだから
我々ソフト屋は口を開けて待っているだけにして
最適化なんてやめてしまえば良いのに

我々が忙しく働くということは、それだけバグを産むということなのだ…!
0803デフォルトの名無しさん (ワッチョイ 82a4-Eq1o)
垢版 |
2017/11/19(日) 07:18:20.90ID:Rb2sIcHm0
CPUだけ速くなってもDRAMの速度が昔のままだ
.7CRなんて法則を持ち出すまでもなく
現実にCPU速度が飽和したと感じたことなどないはずだが
0807デフォルトの名無しさん (ワッチョイ a19f-RjUU)
垢版 |
2017/11/19(日) 13:02:38.85ID:SsMAbqSz0
>>795改造は-fno-strict-aliasing有り無しによらず動くコードが出た。詳細は以下。

https://godbolt.org/g/QaSwTn
-O3 -fno-strict-aliasing -std=c++14 -pedantic -Wall -Wextra で mov QWORD PTR [rsp+8], rbx (27,29行目)が出る。
-fno-strict-aliasing を切っても同じ mov 命令が出る。
だからこれはwarningが出ているだけで動くコードが出る。
このときのコードは以下。
void test() {
double f = 0;
for (long long i = 0; i <= 10; ++i) {
*(long long*)&f = i; // (A)
std::cout << f << std::endl; // (B)
}
}
0808デフォルトの名無しさん (ワッチョイ a19f-RjUU)
垢版 |
2017/11/19(日) 13:04:16.49ID:SsMAbqSz0
>>798
なお上記のGCC結果、或いは以下GCCのドキュメントは読んだ。
(ついでに後出の仕様書もチラ見したが、やはり今のところ君の理解がずれてるという見解だ。)
https://gcc.gnu.org/onlinedocs/gcc-4.4.3/gcc/Optimize-Options.html#index-fstrict_002daliasing-750
ここでは以下コードがアウトだと言っている。
union a_union {
int i;
double d;
};
int f() {
double d = 3.0; // (C)
return ((union a_union *) &d)->i; // (D)
}
アウトな理由だが、上記と794内URLによると、「『型違いのaliasはない』と仮定して最適化」する為であり、
つまり(C)はd名でdoubleに書き(D)はi名でint読み出しだから(C)と(D)は関係ない、
よって(C)はデッドコードでいきなり(D)の読み出ししてよし、ということらしい。

ただこれだとやはり上記(A)(B)は動く。(A)も(B)もfの読み書きであり、aliasして無いからだ。
この規定は「型違いの『aliasは』ないものとみなす」であり、aliasしてなければ関係ない。
今のところ見る限り、他の例も必ずalias(別名でアクセス)している。

なお正しくはunionを使え、ということらしい。>>763
まあ確かにunionはこれ用ではあるが、単発ならCキャストする奴が多いとは思う。
そもそもunionは撲滅対象だと思っていたのだが、これは意外だ。
(或いは仕様上 char*, unsigned char* については許可《どう見ても妥協だが》しているので、void*ではなくchar*にすればいい)
0809デフォルトの名無しさん (ワッチョイ a19f-RjUU)
垢版 |
2017/11/19(日) 13:06:08.29ID:SsMAbqSz0
仕様書は以下でいいか?読み慣れてないからだいぶ推測が入るが、
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf
俺の結論は以下かな。

・gccのは -fstrict-aliasing で、あくまで alias についてだが、仕様書内は alias なんて関係ない。
・fstrict-aliasing 出来る理由が3.10.10によるのなら、reinterpret_castの存在価値はなく、仕様書内に矛盾がある。
・おそらく reinterpret_cast 撲滅で union に書き換えろ、という方向か?
aliasってのは多分折衷案で、union 以外は全部アウトにしたいっぽい。

詳細は以下。

5.2.10 Reinterpret castでは特に妙なところはなし、型キャストは為される。
aliasについては3.10.10だが、reinterpret_castしてれば the dynamic type 扱いでアクセスに問題なし、と見る。
3.10.10.の注 54) The intent of this list is to specify those circumstances in which an object may or may not be aliased.
なんだから、やはりこれは alias されているかどうか?であって、アクセスできるかどうかではない。
ただしここを根拠に undefined behavior だから最適化してよし、としてるのはかなり強引で、
(というかここでalias云々がかなり唐突で、そもそもaliasの話をここではしていない)
(D)がreinterpret_cast扱いだというのなら仕様内に矛盾があることになる。
そしてこの解釈(=reinterpret_castされたものは the dynamic type ではなく undefied behaviorだからどうなってもよし)が通るのなら、
reinterpret_castの存在価値がなくなってしまうし、
aliasとかせこいことを言わず、reinterpret_cast相当のところは全部undefined扱いで削除していいことになる。
というか、多分コンパイラ側はこの主張で、これに対してユーザ側が反対し、
結果、 alias とかいう折衷案でごまかしているように読める。(既に書いたがaliasが唐突過ぎ)
確かに正しくはunionを使うべきであり、この流れだと将来的には reinterpret_cast は廃止で union しろってことになるのか?
しかし逆に言えば、C++89以来これで大して変わらないのなら、早々急に変わることもないか。
0811デフォルトの名無しさん (ワッチョイ 02bd-VFQ0)
垢版 |
2017/11/19(日) 14:19:11.46ID:p3uF8GIb0
コンパイラがうまくType-Based Alias Analysis出来るケースをいくら列挙したところで
Type-Based Alias Analysisできないケースが簡単に生じることは否定できない事実じゃんヤバイじゃん?

unionの使用が解決策というのも語弊があって、unionのメンバのアドレスを取ってから関数foo()に渡す手順だと
foo()のコンパイル時には渡ってきた2つのポインタが、型が違うけど(ループ変数などの条件次第で)同じ領域を指す(ことがある)
かどうか(ループを実際に実行してみねば)判断つかないじゃん?→コンパイラは怖くてループをmemcpy()やmemset()に置き換えられない

つまりunionこそ別の翻訳単位に属する関数に下手な渡り方をするとaliasingのすくつと化す気配が微レ存

元を断つにはやっぱID:xhmNfS4m0やGCC様がおっしゃるように、
そもそも型の混同要因(つまりポインタのキャスト)を絶つか、最適化が効いてほしい関数でプログラマが明示的に__restrictするしか、

※ 個人の感想です
0812デフォルトの名無しさん (ワッチョイ 0233-Wr28)
垢版 |
2017/11/19(日) 14:25:36.63ID:67VHGIrY0
>>808
> ただこれだとやはり上記(A)(B)は動く。(A)も(B)もfの読み書きであり、aliasして無いからだ。

どうも "alias" の有無を名前に基づいて考えてるみたいだけど、
C++ (ほか多くのプログラミング言語)の文脈ではソースコード中で使われる実際の名前は関係ないんだよ。
"*(long long*)&f = i;" は "auto p = (long long*)&f; *p = i;" と等価であり、
これらのうち名前をつけた後者だけがエイリアスの問題があるということはなく、
どちらも f が表すオブジェクトに対してエイリアスがある状態と言う。
(最適化器の気持ちになって考えればわかりやすいんじゃないかと思うんだけど・・・。)

3.10.10 の注が "alias" に言及しているのも、このルールが "aliasing rule" と
呼ばれるのもそういうこと。

> なお正しくはunionを使え、ということらしい。>>763
> まあ確かにunionはこれ用ではあるが、単発ならCキャストする奴が多いとは思う。

union で最後に書いたメンバと別のメンバを読み出すのも C++ では未定義動作だよ。
gcc は( -fstrict-aliasing の説明にもあるとおり)特別ルールを設けてある程度の保証を与えている状態。
https://gcc.gnu.org/bugs/#casting_and_optimization
> To fix the code above, you can use a union instead of a cast (note that this is a GCC extension which might not work with other compilers):
0813デフォルトの名無しさん (ワッチョイ 0233-Wr28)
垢版 |
2017/11/19(日) 14:26:49.64ID:67VHGIrY0
>>809
> ・fstrict-aliasing 出来る理由が3.10.10によるのなら、reinterpret_castの存在価値はなく、仕様書内に矛盾がある。

元の型に戻せば元通り使えることが保証されているので、変更できない既存ライブラリの型に情報を無理やりねじ込んだりするのには使える。
3.10 の例外により unsigned char* などによるオブジェクト表現への直接アクセスにも使える。

> aliasについては3.10.10だが、reinterpret_castしてれば the dynamic type 扱いでアクセスに問題なし、と見る。

まだオブジェクトの型 (dynamic type) と式の型との区別がついてないようだねぇ。
0814デフォルトの名無しさん (ワッチョイ 02bd-VFQ0)
垢版 |
2017/11/19(日) 14:38:42.54ID:p3uF8GIb0
>>810
memset()にわたるbufが__restrictでないポインタでないということは、関数の中で
 memset(p, 0, 1000);
 ...
 memset(p, 0, 1000);
と2回呼んだとき、pが非__restrictだったりグローバル変数だったりするとバカ正直に2回memset()されてしまう…

また、memset()は該当しないが
一般論としてポインタの引数が1つしかなくとも大域変数経由で(一見別のエリアを指すように見える)ポインタを受け取るケースがあるので
1引数関数でも__restrictが要るケースがあるんじゃわ;
0815デフォルトの名無しさん (ワッチョイ a19f-RjUU)
垢版 |
2017/11/19(日) 14:43:08.58ID:SsMAbqSz0
>>811
808のコードで言うとね、

union a_union {
int i;
double d;
};
int f() {
double d = 3.0; // (C)
return ((union a_union *) &d)->i; // (D)
}

がアウトで、

int f(a_union* u) {
u->d = 3.0; // (E)
return u->i; // (F)
}

にしろってことだよ多分。これは>>812内URL内容とも一致する。

>>812
と、ここまではいいとして、これもGCC拡張であり、
> union で最後に書いたメンバと別のメンバを読み出すのも C++ では未定義動作だよ。
これはマジなの?C++のunionのところを読めばいいのか?

> 最適化器の気持ちになって考えればわかりやすいんじゃないかと思うんだけど・・・
これの可能性も考えたんだが、これまで全ての例で必ず別名をつけられてる。
LLVMはあまり知らないが、LLVMにおいてはレジスタへの再代入は禁止のはずだから、
同じレジスタがずっと使われてて最適化器からも「同じだ」と見える可能性もあるよ。
0817デフォルトの名無しさん (ワッチョイ a19f-RjUU)
垢版 |
2017/11/19(日) 14:52:43.00ID:SsMAbqSz0
>>813
3.10.10.4 a type that is the signed or unsigned type corresponding to the dynamic type of the object,
って言ってんだから基礎型も the dynamic type に含まれてると思うぞ。

> 変更できない既存ライブラリの型に情報を無理やりねじ込んだりするのには使える。
使えない。
reinterpret_castの結果がlvalueとして正当ではない、だからundefinedで何やってもおk、
というのが最適化していい根拠なんだろ。だったらreinterpret_castしたその値を使う時点で駄目だろ。
lvalueとして正当に使えると言うのなら、undefinedじゃないんだから最適化で削除しては駄目だ。

ただまあ、ここら辺を俺と君でやりあっても意味が無い。
おそらく20年前にガチで同じことが彼らによって既に為されているはず。
俺がコンパイラを作るわけでも無し、俺は現状確認だけでいいよ。
0818デフォルトの名無しさん (ワッチョイ e1b3-GXP8)
垢版 |
2017/11/19(日) 14:57:33.47ID:PnhOPMpK0
>>814>>816
>一般論としてポインタの引数が1つしかなくとも大域変数経由で
あ、そういうことか・・・
まぁmemset使ってそういう症状出るとしたら、呼び出し側のpでつければいいよね

自分は__restrictそんなに使い倒してないけど、C++の標準に入らないのかな
0819デフォルトの名無しさん (ワッチョイ 0233-dUYE)
垢版 |
2017/11/19(日) 15:03:35.17ID:xhmNfS4m0
>>817
これだけ言われてまだ dynamic type が何なのか読んでないとしか思えない発言が来るか。
https://timsong-cpp.github.io/cppwp/n4659/intro.defs#defns.dynamic.type

> reinterpret_castの結果がlvalueとして正当ではない、だからundefinedで何やってもおk、
> というのが最適化していい根拠なんだろ。だったらreinterpret_castしたその値を使う時点で駄目だろ。

ポインタの reinterpret_cast で型違いの結果を得ることは未定義動作にならないし、
それに * を適用して lvalue を得ることも未定義動作にはならない。
得られた lvalue を通したアクセスが aliasing rule に基づいて未定義動作となる可能性がある。
0820デフォルトの名無しさん (ワッチョイ 02bd-VFQ0)
垢版 |
2017/11/19(日) 15:04:02.65ID:p3uF8GIb0
>reinterpret_castの結果がlvalueとして正当ではない
えっ合法なんじゃ…
 int x = 10;
 char* p = reinterpret_cast<char*>(&x);
 *p = 2; // *pがlvalueであるところの式
 x = (int)*p;  // 元の型へのキャストバック
 printf("x=%d\n", x); // 2

多分最適化されると
 printf("x=%d\n", 2);
になる
0821デフォルトの名無しさん (ワッチョイ a19f-RjUU)
垢版 |
2017/11/19(日) 15:19:05.40ID:SsMAbqSz0
>>812
> union で最後に書いたメンバと別のメンバを読み出すのも C++ では未定義動作だよ。
一応unionのところ(9.5)読んだ。これは書いてなかった。
ただしGCCが「独自拡張だ」って言ってんだからどこかにはあるのだとは思うが。
章番号分かればよろしく。まさか、書いて無いから未定義って奴か?

なお9.5.5ではanonymous union ってのが定義されてて、どうもこれでやれってことっぽい。
例は以下。

void f() {
union { int a; const char* p; };
a = 1;
p = "Jennifer";
}
0822デフォルトの名無しさん (ワッチョイ 02bd-VFQ0)
垢版 |
2017/11/19(日) 15:22:01.05ID:p3uF8GIb0
dynamic_typeはdynamic_cast<>とかtypeidの実装がvtableを悪用した黒魔術なので使わなーい

それはそうとして、Type-Based Alias Analysisの障害となるaliasingだけことさら問題視するLLVMの人らのスタンスはフェアではないキモス
同じ型でもaliasingは起こそうと思えばいくらでも起こせるし、コンパイラはプログラマーが意図的にaliasingさせるケースが有り得るのではと
猜疑心にとらわれて十分な最適化ができないのは同じ

やっぱC++は関数型プログラミング言語にシフトすべき頃合い
0823デフォルトの名無しさん (ワッチョイ a19f-RjUU)
垢版 |
2017/11/19(日) 15:27:33.98ID:SsMAbqSz0
>>819
3.10.10には the dynamic type と cv-qualified version of the dynamic type しかないのだが、
では double や long long は何型なのだ?

> 得られた lvalue を通したアクセスが aliasing rule に基づいて未定義動作となる可能性がある。
ではその aliasing rule を読もう。何章だ?
なんとなく aliasing rule はコンパイラ側の仕様で、C++の仕様では無いと思うのだが。
0824デフォルトの名無しさん (ワッチョイ 0233-dUYE)
垢版 |
2017/11/19(日) 15:35:56.40ID:xhmNfS4m0
>>823
なんでせっかくリンク貼られた定義を無視するの?(・・・英語まともに読めないならそう言ってね。)
dynamic type は式を評価した結果の属性であって型の分類ではない。

aliasing rule は basic.lval にあるルール "If a program attempts to access ... the behavior is undefined" を指す。
https://timsong-cpp.github.io/cppwp/n4659/basic.lval#8
0826デフォルトの名無しさん (ワッチョイ a19f-RjUU)
垢版 |
2017/11/19(日) 15:39:23.48ID:SsMAbqSz0
>>822
違うぞ。
794内リンク読む限り、確かにこの最適化はフェアだし、使われていい。
というか、それ以前はポインタが一つでもあったら全く最適化が出来なくなっており、
確かにそれは問題視されてた。
だから標準化委員会がそっちを選んだ、というのは分かる話だ。
違う型でエイリアスすることはほぼないし。

問題は意図的にやりたいことが偶にあって、そのときにどう回避するかだよ。
763がこれに該当する。
0827デフォルトの名無しさん (ワッチョイ 0233-dUYE)
垢版 |
2017/11/19(日) 15:51:01.10ID:xhmNfS4m0
>>826
現行の規格で保証される範囲でまともな方法を見出そうとしてるなら、苦労に見合う実りは得られないからやめとけと言っておく。
標準化委員会のおおかたもそんな認識だから bit_cast なんていう提案が好意的に進められている。
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0476r1.html
0828デフォルトの名無しさん (ワッチョイ a19f-RjUU)
垢版 |
2017/11/19(日) 15:53:50.85ID:SsMAbqSz0
>>824
では最初から分かるようにそう言え。

こちらは仕様書を読むのが仕事ではないので、そもそも用語も知らない。見て分からなければそれまでの話だ。
つか、lvalue, rvalue もひどいが、 xvalue とかもでてきて、いよいよC++はどうしようもなくなりつつあるなと実感したよ。
Linusが嫌うわけだよこれは。

ただまあこれは本題でもない。 aliasing rule の確認の方が重要だ。
してそのリンク先、3.10.10と同じだ。

> 得られた lvalue を通したアクセスが aliasing rule に基づいて未定義動作となる可能性がある。(>>819)
これはどこからそう取れるんだ?
reinterpret_castをした結果が正当なlvalueなら、それをデリファレンスした結果も正当なlvalueだろ。
この章にはどこにも”aliasしてる場合”なんて記述はない。
だらからここを根拠にするのがそもそもおかしいんだよ。

君が期待することがここに書いてあるとしたら、例えば以下になるべきなんだよ。

the dynamic type of the object IF THE OBJET IS NOT ALIASED,

とかね。でも実際はそうじゃない。
0829デフォルトの名無しさん (ワッチョイ a19f-RjUU)
垢版 |
2017/11/19(日) 16:10:52.86ID:SsMAbqSz0
>>827
つか、完全に暴走してるなこれは。

これならmemcpyと同じだし、
わざわざ別に作らずともmemcpyを正式に認めてCと同じ並びにしたほうがいい。
それ以前に reinterpret_cast が使われている関数内は自動的に -fno-strict-aliasing すればいいだけなんだが。
(関数単位でこの最適化を切る。ほぼ全てこれでいけるはず)

ただまあ、これが真面目に議論されているのなら、仕様書の文面や俺の感想はともかく、
君の言う通り、今のC++の仕様では対処する方法がない、ということなのだろうね。
0830デフォルトの名無しさん (ワッチョイ 0233-dUYE)
垢版 |
2017/11/19(日) 16:11:41.87ID:xhmNfS4m0
>>828
"If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined"
これが lvalue (それ自体は未定義ではない)を通した「アクセス」を未定義とするルールとして読めないのだとしたら、もうどう説明しても無理だろう。
用語を知らないといいながら共通の定義を求めるのではなく開き直って勝手な解釈に基づく話を進められるようではまともな会話にもならない。
説明は諦める。

規格や先人の解説を読みなおすなりして誤りに気づいてもらえれば幸いだ。
https://www.google.com/search?q=aliasing+rule
0831デフォルトの名無しさん (ワッチョイ a19f-RjUU)
垢版 |
2017/11/19(日) 16:30:16.51ID:SsMAbqSz0
>>830
ちげーよ。
俺は、reinterpret_castの結果が正当なlvalueなら、それはド頭の
the dynamic type of the object
に該当するから、
*(long long*)&f = i
はありだろ、と読んでるんだよ。そこは君も同じだろ。

> ポインタの reinterpret_cast で型違いの結果を得ることは未定義動作にならないし (>>819)
つまり (long long*)&f で long long* 型になるのはおk ←君の見解ね、俺も同意だが。

> それに * を適用して lvalue を得ることも未定義動作にはならない。
得られた lvalue を通したアクセスが aliasing rule に基づいて未定義動 (>>819)
つまり、*(long long*)&f もおk ←これも君の見解ね、俺も同意で。

> 得られた lvalue を通したアクセスが aliasing rule に基づいて未定義動作となる可能性がある
ここが違う。
3.10.10には alias が云々って何も書いてないだろ。
しかも *(long long*)&f = i; は両方とも long long なんだから、「型違い」のaliasではないんだよ。
だからGCC等の -fstrict-aliasing には該当しない。

というのが文面からとれる意味だ。あくまで「文面」な。
ただまあ、実際にはそうじゃないんだろ。
0833デフォルトの名無しさん (ワッチョイ 0233-dUYE)
垢版 |
2017/11/19(日) 16:34:35.29ID:xhmNfS4m0
>>830
> 俺は、reinterpret_castの結果が正当なlvalueなら、それはド頭の
> the dynamic type of the object
> に該当するから、
> *(long long*)&f = i
> はありだろ、と読んでるんだよ。そこは君も同じだろ。

違うねぇ。
オブジェクトの型 dynamic type は式の型とは違うし、式の型の影響を受けるものじゃないんだよ。
何度も区別しろと言ったつもりだったんだけど。
0835デフォルトの名無しさん (ワッチョイ 82ae-Eq1o)
垢版 |
2017/11/19(日) 16:43:31.03ID:7jEiMXMd0
>>825
確かに自分に中身のないやつほどルールブックを神格化するよな
自分なりにどう思うなんて怖くて言えないから
で、そういうお前さんは何か内容のある発言ができるのかね?
0837デフォルトの名無しさん (ワッチョイ c6e7-RjUU)
垢版 |
2017/11/19(日) 17:22:20.65ID:XAwzlQ9S0
double f = 0;
と変数fを構築した時点で、「値0.0のdoubleオブジェクト」が作られてメモリに置かれるんだよ(規格で言うstored valueな)
それはスタック回収やdeleteで破壊されるまでずーーっとdoubleのままで、変わることはないの
外側でポインタや参照をどれだけいじくり回したって決して変わらないの

家の横に「←ここは馬小屋です」って看板を立てたって、その家は家のままだし、勝手に馬を入れて飼うのは未定義動作なんだよ
そろそろ理解したらどうなの
0838デフォルトの名無しさん (ワッチョイ c58a-iF25)
垢版 |
2017/11/19(日) 17:56:59.79ID:BMNBgd4s0
>>835
内容ある発言と言われても別にこんなのは規格をひっくり返して議論するようなことでないし
たぶん俺なら(little endianだとして)
union Double {
double v;
struct {
uint64_t f:52; // fraction
uint64_t e:11; // exponent
uint64_t s:1; // sign
};
};
とかにするかな、それで何の問題も起こらない
0839デフォルトの名無しさん (ワッチョイ a19f-RjUU)
垢版 |
2017/11/19(日) 18:00:09.08ID:SsMAbqSz0
>>837
多分それが違うぞ。それはどこに書いてある?

少なくとも多くのCプログラマはそう思って無い。だから平気でキャストする。
そして830内ULRも読んだが、これも明示的なエイリアスがあるケースだ。
お前らが過度にびびって拡大解釈してるだけだろ。
0840デフォルトの名無しさん (ワッチョイ 82ae-Eq1o)
垢版 |
2017/11/19(日) 18:11:47.43ID:7jEiMXMd0
あー、こいつcast-as-lvalueに完全に洗脳されている手合いかw
0842デフォルトの名無しさん (ワッチョイ a19f-RjUU)
垢版 |
2017/11/19(日) 18:31:26.29ID:SsMAbqSz0
>>841
嘘つけ。キャストした場合についてはどこにも書いてない。
型が普遍なんてのは、君が勝手に思い込んでいるだけだ。
というか、いわゆる「強い型」ってのはそうらしいが、C++はそうじゃないだろ。


ちなみに俺も反論を用意してたところだ。
https://godbolt.org/g/viYhGj

中身は795の場所で、ついでだから f = *(double*)&i; を試した。
予想通りこちらはfmov(movsd)が出た。(最適化は切ってある)
つまり、俺が768で言ったように、

*(long long*)&f = i; // mov命令でコピー
または
f = *(double*)&i; // fmov命令でコピー

になるんだよ。少なくともgccはキャストされればその型だと認識している。
君はこれを矛盾無く説明出来ないだろ。
0843デフォルトの名無しさん (オッペケ Srd1-0Zlk)
垢版 |
2017/11/19(日) 18:51:00.89ID:Q6cWJ/Rgr
>キャストした場合についてはどこにも書いてない

はて、N4700の6.10/p8ほぼそのままの文面が少し上に貼り付けてあったような…

>>821
N4700 3.9, 4.5/p1, 6.8全般, 6.10/p8, 12.3/p1, 12.3/p(5.3), その他「launder」が登場する記述全般
0844デフォルトの名無しさん (ワッチョイ 02bd-VFQ0)
垢版 |
2017/11/19(日) 18:52:07.63ID:p3uF8GIb0
正しいそうなコードが吐かれたことは未定義動作でないという証拠には全くならない
なぜなら未定義動作というのは
コンパイル時のエラー、実行時のエラー(または例外)、一見正常っぽく気体通り動く、その他、
というあらゆる事象を包含し得るので、、、

もちろんコンパイラの変更で(ことによったらコンパイル条件の変更だけでも)
ある日突然狂ったコードが吐かれる危険性があるが
そうなっても未定義動作をプログラムした人の責任
よって現時点で正しいげなコードが吐かれることをいかに力説しても無駄で、
規格の矛盾の指摘にはつながらない
0845デフォルトの名無しさん (ワッチョイ 0233-dUYE)
垢版 |
2017/11/19(日) 18:53:33.74ID:xhmNfS4m0
>>842
オブジェクトの型に関与しない操作についていちいち記述があるわけないだろ。
まさか「〜によりオブジェクトの方は変わらない」とかの記述が全演算子&標準関数その他諸々について必要だ
なんて言うわけじゃないよね?

未定義動作の結果はなんでもアリだと言っただろう。
その結果(想定の命令コードが生成されたこと)が未定義動作の結果のひとつだとして、何の矛盾も無い。
0847デフォルトの名無しさん (ワッチョイ f9b3-AWKa)
垢版 |
2017/11/19(日) 19:04:38.54ID:RgqRbH7C0
ソースが仕様、ソース読めってやつか。
0848デフォルトの名無しさん (ワッチョイ 82ae-Eq1o)
垢版 |
2017/11/19(日) 19:25:00.73ID:7jEiMXMd0
そう言って居直るやつは問題だが
ソースを読まない言い訳にするやつもダメ
腐敗は両岸から起きうることだ
0849デフォルトの名無しさん (ワッチョイ a19f-RjUU)
垢版 |
2017/11/19(日) 19:53:33.03ID:SsMAbqSz0
>>844-845
うむ。それは一理ある。

>>845
では逆から行こう。reiniterpret_castのど頭、
> 5.2.10 Reinterpret cast
> 1. The result of the expression reinterpret_cast<T>(v) is the result of converting the expression v to type T.
vをTに型変換したのが結果だとそのまま書いてある。
これはどう解釈すれば、「型はどうやっても変更できない」と取れるのだ?
さすがに無理だろ。

> 7. An object pointer can be explicitly converted to an object pointer of a different type.
これも型を変更できると読めるが。そして再び1に戻ると、

> 1. ---- If T is an lvalue reference type or an rvalue reference to function type, the result is an lvalue;
つまり当然有効なlvalueであり、デリファレンスも可能だ。
だからキャストを反映してmovなりfmovが出るのだ。
違うか?
君が言うようにメモリ上の型が普遍なら、どうやっても i には fmov が出ては駄目だろ。
(と思ったが、君は837ではないのか、、、)
0850デフォルトの名無しさん (ワッチョイ 82ae-Eq1o)
垢版 |
2017/11/19(日) 20:02:38.24ID:7jEiMXMd0
>>849
If T is an lvalue reference typeという但し書きがついているよな
(double&)ならlvalueだが(double)は違う
0851デフォルトの名無しさん (ワッチョイ 0233-dUYE)
垢版 |
2017/11/19(日) 20:09:33.36ID:xhmNfS4m0
>>849
> これはどう解釈すれば、「型はどうやっても変更できない」と取れるのだ?

式の型 (static type) は変わるが、オブジェクトの型 (dynamic type) は変わらない。

> 君が言うようにメモリ上の型が普遍なら、どうやっても i には fmov が出ては駄目だろ。

未定義動作ならなんでもアリだと言った。
0852デフォルトの名無しさん (ワッチョイ a19f-RjUU)
垢版 |
2017/11/19(日) 20:15:36.23ID:SsMAbqSz0
>>851
それで突っぱねるのなら平行線だね。
俺は仕様書に詳しいわけでは無いから動作から説明するしかない。
君は仕様書には詳しいようだが、キャストした場合にも型が変更されないという記述を出して来れない。
ま、デッドロックだ。

とはいえ状況は色々分かったよ。ありがとう。
0853デフォルトの名無しさん (オッペケ Srd1-0Zlk)
垢版 |
2017/11/19(日) 20:42:27.06ID:Q6cWJ/Rgr
>つまり当然有効なlvalueであり、デリファレンスも可能だ。

仕様を引用して説明されてもこのとんちんかんな発言を繰り返すあたり、
知らないから間違えているのではなくオツムが悪いので必然的に無知になったと言うことに>>851は気付くべき
0854デフォルトの名無しさん (ワッチョイ 0233-dUYE)
垢版 |
2017/11/19(日) 20:47:51.98ID:xhmNfS4m0
>>852
> 俺は仕様書に詳しいわけでは無いから動作から説明するしかない。

なら↓お前の理解に基づいてこの動作を説明できるか試してみるといいかもしれない。
https://wandbox.org/permlink/2aKzO4VN3KRDB6Gu

> 君は仕様書には詳しいようだが、キャストした場合にも型が変更されないという記述を出して来れない。

式の型 (static type) とオブジェクトの型 (dynamic type) は別物だという客観的な
前提 [intro.defs] を素直に読みさえすれば何度も見えてるはずなんだけどな。
0858デフォルトの名無しさん (ワッチョイ 0233-dUYE)
垢版 |
2017/11/19(日) 21:40:24.30ID:xhmNfS4m0
>>857
お前の中でどういう理解になっているかは把握できていない。

その答えからすると、
"*(long long*)&f = i;" なら未定義動作にならないけど "auto p = (long long*)&f; *p = i;" なら未定義動作になる、
という理解なのかな。まぁそんなことは無いんだけどさ。
0859デフォルトの名無しさん (ワッチョイ f9b3-AWKa)
垢版 |
2017/11/19(日) 22:14:48.53ID:RgqRbH7C0
いやまあ無いんだけどな。
いつまで言い張る気なんだろうか。
0860デフォルトの名無しさん (ワッチョイ f9b3-AWKa)
垢版 |
2017/11/19(日) 22:16:28.57ID:RgqRbH7C0
あると言えばあるんだけどな。
そろそろごめんなさいしてはどうか。
0862デフォルトの名無しさん (ワッチョイ a980-61Vg)
垢版 |
2017/11/19(日) 23:33:53.04ID:qbUCqX2r0
strict aliasing ruleについてはここで解りやすく解説されてる。自分で怪しいと思う奴は読め。
http://d.hatena.ne.jp/yohhoy/touch/20120220/p1
0863デフォルトの名無しさん (ワッチョイ a19f-RjUU)
垢版 |
2017/11/19(日) 23:37:47.80ID:SsMAbqSz0
>>858
ああそうだぞ。それは>>815で既に言ったし、実際に発現事例は今のところ全てaliasされてる。
顕在化させるには、「別型」で「alias」しないといけないんだよ。
オプションの名前も全くそうだろ。

多分君は理屈が分かってないんだ。
あれはtypeグループごとにメモリフェンスしていて、
別typeグループに入っていると結果的にout-of-orderになるだけなんだよ。
それは常時動いている。
ただし different type で alias して無いと顕現しない(ユーザ側には見えない)というだけ。
だからその可能性がある部分にwarningが出る。
しかしwarningというのは「普通やらないけど大丈夫か?」であって「絶対駄目」ではない。
結局君は different type alias について正確に理解出来てないんだよ。

上記を覆したければ、alias無しで different type alias の最適化がかかっている例を持って来い。
君の言う、
> まぁそんなことは無いんだけどさ。
の例だね。多分無いから。

既に言ったがLLVMだからインデックスレジスタは完全に残るんだよ。(多分)
キャストを知らないのだから理解出来ないようだが、(long long*) 部分は命令にならない。
(これはC++仕様書にも明記してあるが、C界では常識)
だから *&f だけが残り、結果、
*(long long*)&f と *&f (=f) は同じLLVM命令になる。(はず)
だからそこは問題視されてないんだよ。最適化器からも同じに見えるから。

逆に考えてみろ。同じtypeグループ内、例えば int と int が out-of-order になったら話にならんだろ。
だから同typeグループ内は alias があっても in-orderになってる。
そして別グループ double と int は常にout-of-orderになってるが、当然、関数でつながれているときにはフェンスされる。
例えば int a = max_idx(double*) みたいな感じで、doubleの配列から最大値の添字を取るときとかね。
だから different type で alias して無いとそれが見えないようになってるんだよ。
というのが俺の理解だ。覆す例があればよろしく。
0866デフォルトの名無しさん (ワッチョイ f9b3-AWKa)
垢版 |
2017/11/20(月) 00:06:21.48ID:Veq8ZMOx0
最初に質問した奴が謝罪すれば治まるのでは。
0868 ◆QZaw55cn4c (ワッチョイ e260-v9tW)
垢版 |
2017/11/20(月) 00:14:41.52ID:Y8ntE/6M0
いきなりレベルの低い話で割り込んで恐縮です。
よろしくお願いいたします。

久々にJava から C++ に移行して、簡単なイテレータパターンを実装しました。
Java で書いた
https://ideone.com/DJ9pI5
を元に C++ で書いてみたのがこれです。
https://ideone.com/FwNlc4

ここで、コンテナにあたる Aggregateクラス(具象クラスは BookShelf) の中に
イテレータ(Iterator:具象クラスは BookShelfIterator) を作成して返すメンバ関数 iterator() を定義していますが、
C++ だからデストラクタも定義しないとね、と考えて
当初、Aggregate クラスに仮想メンバ関数 delete_iterator() を書いて
Aggregate::iterator() と Aggregate::delete_iterator() を対にするように作っておりました。

しかし教科書をみると
Aggregate::delete_iterator() みたいなデストラクタはそもそも定義しないようで、
普通に
Iterator *it = BookShelf(具象クラス)->iterator();
delete it;
でなんの問題もないようです。

なぜ、これで問題がないのかトンと見当がつきません。
イテレータを作った具象クラスの中でデストラクタを定義する、というのならば分かりやすいのですが、
抽象クラスのポインタをキャストもせずに直接 delete できるのは、どういうからくりになっているのでしょうか?

よろしくお願いいたします。
0871デフォルトの名無しさん (ワッチョイ a19f-RjUU)
垢版 |
2017/11/20(月) 00:53:02.54ID:H8zFncfx0
>>869
せこいわw
gcc7.2.0(そこの最新版)で%eにしたら見えるがな。

まあ努力はご苦労様。
とはいえ、gcc4.4.7ならこのコードでも顕在化するし、「aliasが要る」という要件が怪しいのは認める。
この最適化はまだ発展途上のようだね。
0872デフォルトの名無しさん (ワッチョイ a19f-RjUU)
垢版 |
2017/11/20(月) 01:07:57.23ID:H8zFncfx0
>>870
おっとすまん、輻輳してしまったが、869で問題なく分かったぞ。
そして両方とも、gcc7.2.0にするか、globalにすると直る。

したがって、 different type alias で問題が発生するのには 「aliasが要る」という俺の見解は間違いだ。
ただ、過度の最適化による不具合を防ぐ努力は行われており、
gcc4..4.7→gcc7.2.0でこのコードに関しては修正されてる。
とはいえgcc7.2.0でもどこまで動くか分かった物ではないね。
0874デフォルトの名無しさん (ワッチョイ f9b3-AWKa)
垢版 |
2017/11/20(月) 01:32:56.30ID:Veq8ZMOx0
何で素直に謝罪できないんだろうね。
0875はちみつ餃子 ◆8X2XSCHEME (ワッチョイ 466f-RuWE)
垢版 |
2017/11/20(月) 01:42:11.88ID:mEO8F9pQ0
>>868
それが virtual の効果だとしか。
基底で virtual 宣言したメンバ関数群はそのポインタをまとめたテーブルが作られ、オブジェクトはそのテーブルを指すポインタを持つ。
派生のオブジェクトは派生側の関数群を指すポインタテーブルへのポインタを持つ。
だから virtual を使うと「変数の型ではなくオブジェクトが呼出すべきメンバ関数を知っている」という状況を作れるんだよ。

ただ、基底のデストラクタを virtual に指定しないときでも見かけ上は型システムをパスしてしまいコンパイルエラーにならない場合がある。
これは未定義だからやっちゃダメだよ。
0877 ◆QZaw55cn4c (ワッチョイ e260-v9tW)
垢版 |
2017/11/20(月) 02:35:52.58ID:Y8ntE/6M0
>>875-876
うーん、普通のポリモフィズムの話が同様に適用できるんですね…
Base<-Derived1
Base<-Derived2
の基底・派生関係のとき
Base *p = new Derived1() ならば、delete p; は delete (Derived1 *)p;
Base *p = new Derived2() ならば、delete p; は delete (Derived2 *)p;
となる、と考えていいでしょうか。
Base *p = new Derived1() ならば、p->method(); は p->method_Dirived1();
Base *p = new Derived2() ならば、p->method(); は p->method_Dirived2();
という話と同じなんですね。

んーなせ Aggregate::delete_iterator() を作らなくちゃ、と考えたのか逆にわからなくなってしまいました…
ありがとうございました。
0878 ◆QZaw55cn4c (ワッチョイ e260-v9tW)
垢版 |
2017/11/20(月) 02:59:04.07ID:Y8ntE/6M0
次に >>868 のテンプレート版を書いてみました。
https://ideone.com/LO68r5

void * (Java の Object)をキャストするのを嫌ったという理由になります。
>>868
>class Iterator
>virtual void *next() = 0;
>class BookShelfIterator : public Iterator { };
>void *BookShelfIterator::next()
>Book *book = (Book *)it->next();

https://ideone.com/LO68r5

そこで不思議に思ったことなんですが、
コンストラクタは
template <typename T> class Derived : Base { public: Derived(){}; };
とテンプレート引数 T をコンストラクタ名をつけなくていい(public: Derived<T>(){}; としない)のに、
デストラクタは
template <typename T> class Derived : Base { public: ~Derived<T>(){}; }; (クラスのメンバ関数宣言)
template <typename T> Derived<T>::~Derived<T>() { } (クラスのメンバ関数定義)
とテンプレート引数 T をデストラクタ名につけないといけないのでしょうか?何か分かりやすい理由はありますでしょうか?
0879はちみつ餃子 ◆8X2XSCHEME (ワッチョイ 466f-RuWE)
垢版 |
2017/11/20(月) 03:03:42.40ID:mEO8F9pQ0
>>877
キャストとは意味が違うんだよ。
上でさんざん議論してる static type と dynamic type の話とも関係あるのかなぁ?
入り組んでてよくわからん。
型はあくまでも Base* で、仮想関数テーブルを辿って必要な関数を呼び出すってだけだ。

繰返すけど、 Base のデストラクタに virtual がついてないときに Base のポインタに対して delete すると
Base のデストラクタが呼び出されるだけになってまうので気をつけてな。
派生クラスのデストラクタが空っぽのときでもデストラクタが呼び出される必要はあるらしいぞ。
0880 ◆QZaw55cn4c (ワッチョイ e260-v9tW)
垢版 |
2017/11/20(月) 03:12:59.68ID:Y8ntE/6M0
>>878 の続きです。
同じく、テンプレート版
https://ideone.com/LO68r5

にて一点不満におもったことがあります。

>>878 では基底クラスの記述について、
template <typename T>
class Iterator {
public:
virtual T *next() = 0;
};
派生クラスは
template <typename T>
class XXXIterator : Iterato<T> {
public:
T *next();
};
template <typename T> T *XXXIterator::next() { ... }
と書きました。

しかし本当は、基底クラスを純粋に一個にしたいと希望しているのです。
つまり基底クラスにテンプレート引数Tを書きたくない。

class Iterator {
public:
virtual ○○○ *next() = 0;
};
と基底クラスを一つだけにしぼりつつ、
派生クラスにテンプレート引数を使用して記述することは可能でしょうか?○○○に書けるなにかいい記述はないでしょうか?
テンプレート引数を使用した具象クラスに対応する、テンプレート引数を使用しない抽象クラスを書くことは可能でしょうか?
0883 ◆QZaw55cn4c (ワッチョイ e260-v9tW)
垢版 |
2017/11/20(月) 03:26:59.22ID:Y8ntE/6M0
>>879
ありがとうございます。
meyers の effective C++ 第7章を読み返しています
C++ には Java の fainal がないので、うっかり、std::string や std::vector を派生させてしまうところでした、危ないなあ…
0884デフォルトの名無しさん (ワッチョイ 869f-GXP8)
垢版 |
2017/11/20(月) 03:30:13.52ID:iECo0Ul80
>>868
混乱するならシンプルに設計したら?例えばこんな感じ?登録して表示するだけなら簡単でしょ?
#include <stdio.h>
#include <string>
#include <vector>
class Book{
public:
std::string name;
Book(std::string _name) : name(_name){}
void show(){printf("%s\n",name.c_str());}
};
class Books{
public:
std::vector<Book *>m_list;
Books(){}
~Books(){for (auto p : m_list) if (p) delete p;}
void addBook(Book *p){m_list.push_back(p);}
};
int main(){
Books books;
books.addBook(new Book("aa"));books.addBook(new Book("bb"));
books.addBook(new Book("cc"));books.addBook(new Book("dd"));
for (auto p : books.m_list) p->show();
// iterator使いたいなら
//for (auto it = books.m_list.begin(); it != books.m_list.end; it++) (*it)->show();
return 0;
}
0885はちみつ餃子 ◆8X2XSCHEME (ワッチョイ 466f-RuWE)
垢版 |
2017/11/20(月) 03:34:54.09ID:mEO8F9pQ0
>>880
Java のことはあまり知らんので元ネタにした Java 版の方をよく見てなったけど、
interface のかわりに抽象クラスを使おうとしてたのか。
似たような機能に見えちゃうかもしれないけど、インターフェイスを強制する方法としては抽象クラスはあまり良くない。

C++ 的にはコンパイル時にディスパッチする多相と実行時の多相があって、
抽象クラスは実行時にディスパッチする仕組みなのでコンパイル時に確定するはずのことを実行時にするのはクソザコという風潮。

だけど「コンセプト」の機能が C++ に導入されるのが延び延びになってていまだ入ってないので、
traits とか SFINAE とかを使ったまわりくどいメタプログラミングで代用してるのが実情なんだ。
0887はちみつ餃子 ◆8X2XSCHEME (ワッチョイ 466f-RuWE)
垢版 |
2017/11/20(月) 03:53:09.38ID:mEO8F9pQ0
>>886
いや、型名の方には付ける必要あるよ

×:
template <typename T> TShelf::~TShelf() { delete [] (this->t_s); }

〇:
template <typename T> TShelf<T>::~TShelf() { delete [] (this->t_s); }

△: 知らんかった
template <typename T> TShelf<T>::~TShelf<T>() { delete [] (this->t_s); }
0888 ◆QZaw55cn4c (ワッチョイ e260-v9tW)
垢版 |
2017/11/20(月) 03:57:21.44ID:Y8ntE/6M0
>>884
はい、すごくよくわかります!!
std::vector とか、std::list とかstd::deque とかは、たぶんいけると思います。
http://mevius.2ch.net/test/read.cgi/tech/1434079972/14
http://mevius.2ch.net/test/read.cgi/tech/1434079972/19

なんとなくデザパタ本を読み返していて、車輪の再発明に没頭してしまいましたが、C# のデザパタ本、というのも聞かないし、もうデザパタは古いのかな…
0892 ◆QZaw55cn4c (ワッチョイ e260-v9tW)
垢版 |
2017/11/20(月) 04:12:32.38ID:Y8ntE/6M0
>>885
夜遅くにコメントいただきありがとうございます。

>C++ 的にはコンパイル時にディスパッチする多相と実行時の多相があって、
理解できます。コンパイル時に確定できるのなら最大限それに努める思想はたとえば constexpr にも現れていると思いました。

>抽象クラスは実行時にディスパッチする仕組みなのでコンパイル時に確定するはずのことを実行時にするのはクソザコという風潮。
うーん、たぶん私が周回遅れなだけだと思いますが、じゃあ、ひところ、あれほどまでにもてはやされたデザインパターンは、どこにいってしまったのでしょう…もう誰もやらないのかな…
0893はちみつ餃子 ◆8X2XSCHEME (ワッチョイ 466f-RuWE)
垢版 |
2017/11/20(月) 04:13:43.35ID:mEO8F9pQ0
>>887
なんでこうなってるかっていうのは割と感覚的にやっちゃってるからうまいこと説明できひんのやけど。
クラステンプレートの中に関数テンプレートも書けたりするので

template<class T>
class foo {
public:
template<class U> void bar(void);
};

実装書くときはどれがどれに掛かってるかわかるようにせんといかん

template<class T> template<class U>
void foo<T>::bar(void) {
}

みたいな話なんじゃないかとコンパイラの気持ちになる仁奈ちゃんの気持ちになってた。
0894はちみつ餃子 ◆8X2XSCHEME (ワッチョイ 466f-RuWE)
垢版 |
2017/11/20(月) 04:30:34.82ID:mEO8F9pQ0
>>892
抽象クラスを使うパターンなんてのがあるわけ? GoF 本とか読んだことないから知らんけど。
それはインターフェイスをまとめた何かを作れって話とかじゃないの?
C++ の抽象クラスを使えって話とは違ったりしそうだけど。
デザインパターンはあくまでデザインの話なんだから言語の具体的な機能はその時代に有るものでやるしかないし、
よりよい機能が提供されるようになったら使うだろ、そりゃ。

抽象クラスを使うやり方しかできないなら「デザイン」を学び取れてないってこった。
まあ今は C++ への理解も十分でないからってのもあるだろうけどさ。

ところで QZ って C++ スレの常連みたいに思ってたけど、
その割にアレなのでひょっとして共通トリップだったりするの?
0896デフォルトの名無しさん (ワッチョイ 228d-zvir)
垢版 |
2017/11/20(月) 07:05:19.03ID:8Dez9ldp0
手元に古いGoF日本語訳(1999年初版)があったので覗いてみたら、
Iteratorのサンプル(C++)が>>880みたいに抽象クラスで実装されていてびっくりした。

抽象クラスを使わなくても、処理の共通化を考えないなら問題ないし、
共通化したい場合でも、そこだけテンプレート化すればよいような気がする。
https://ideone.com/sFzBq4
0897デフォルトの名無しさん (ワッチョイ 4178-kX9V)
垢版 |
2017/11/20(月) 08:00:57.16ID:GfOPAtNP0
VC++とか今でこそまともだけど昔はテンプレート周りがうんこすぎて風呂釜洗剤教の人のやり方に頼らざるを得なかったらしいね
0898デフォルトの名無しさん (ワッチョイ 826e-Eq1o)
垢版 |
2017/11/20(月) 09:24:05.14ID:9q5rB25R0
そういう「まとも」を求めてVCを選ぶのは情弱のすることだ
意味の違う「まとも」で定評のあるコンパイラであって
0899デフォルトの名無しさん (ワッチョイ f9b3-AWKa)
垢版 |
2017/11/20(月) 09:46:04.69ID:Veq8ZMOx0
Vistaまではgccでコンパイルしてたらしいからな。
0900デフォルトの名無しさん (アウーイモ MM05-0x9U)
垢版 |
2017/11/20(月) 10:17:12.94ID:5dn4op1oM
初心者なんですがusing使い方を教えて下せえ
レス数が900を超えています。1000を超えると表示できなくなるよ。

ニューススポーツなんでも実況