1read 100read
2012年07月プログラム109: 【注意】STLの落とし穴【危険】 (918) TOP カテ一覧 スレ一覧 2ch元 削除依頼
C#, C♯, C#相談室 Part76 (952)
インテルC++コンパイラ9.0発表! (586)
【O3D】HTML5用 3D API WebGL 【Canvas:3D】 (461)
小学生プログラマだけど質問ある (236)
△△もっとStruts2の良さを教えてくださいSsssion6 (637)
C言語をやりたいんですが (261)

【注意】STLの落とし穴【危険】


1 :04/12/27 〜 最終レス :2012/10/27
STL(あるいはboost)を使っていく上で
思わぬ落とし穴、やってはいけない禁じ手などを
晒すためのスレです。暗黙知の解放!

2 :
vector<auto_ptr>

3 :
vectorやstringで、.clear()したからといって
メモリも解放されるとは限らない罠。

4 :
>>2
ん!?危険なの?ちと想像してみよかな・・・

5 :
>>4
std::sort()などのコピーを伴うアルゴリズムを呼び出した時の事を考えてみろ。
普通ポインタのコピーをしても、ポインタが指しているインスタンスは変化しないという
考えの元に各種アルゴリズムが構築されている。
しかし、std::auto_ptrは、ポインタをコピーするだけで、元のポインタの値は 0 に設定
されてしまう。これにより、複数のポインタから同一のインスタンスを指す事のないように
してあるのがstd::auto_ptrだ。だからこそstd::auto_ptrのスコープを外れた時点で、それ
が指すインスタンスもdeleteできる。
すなわち、コピーコンストラクタと代入演算子のセマンティックスが独自の物で、通常の
ポインタとは全く互換性がない。

6 :
gcc3以降では、auto_ptrをstlコンテナに入れてるソースは
コンパイルエラーになるようになってるしね。

7 :
関連?重複?
C言語の見えないバグ、対処法でも語るスレ
http://pc5.2ch.net/test/read.cgi/tech/1064482029/l50

8 :
>>7
C厨と合流するつもりはない。

9 :
auto_ptrはほとんど使わないな。
非常に使いにくい。

10 :
落とし穴つったらアレだろ
コンパイラが対応してないwwwwww

11 :
>>10
STLもまともに機能しない糞コンパイラなぞ捨ててしまえ。

12 :
糞スレ立てんなよ、バーカ R>>1

13 :
EffectiveSTLに書いてある以外の落とし穴があるなら
是非知りたいけど

14 :
>>6
それは、普通でしょう。 auto_ptrはstlのコンテナとは共存できない。
Exceptional C++: Item 37. AUTO_PTR を読みましょう。
ちなみに、auto_ptrをコンテナで使おうとするとエラーが出るのは、Exceptional C++によれば、
auto_ptr's copy constructor and copy assignment operator take references to non-const to
the right-hand-side object. Most implementations of the standard containers' single-element
insert() functions take a reference to const, and hence won't work with auto_ptrs.
とのこと。 boostのshared_ptrは便利だよ。

15 :
stringの参照渡しではなく、実体渡しってコスト高いのかな・・・
例えば、1000文字程度の文字列が格納されてるstringを関数の引数などで
実体渡しだとか、返却すると高いのかな?
string tail( string a)
{
return a + ":addtail";
}
みたいなコード。
参照渡しって微妙に使いにくいんだよね・・・だから実体渡しにしたいの
だけど。

16 :
>>15
stringの実装方法による。大抵のstringは文字列そのものを別の領域にもっており、
コピーコンストラクタでコピーされるのは管理領域だけという場合が多い。
それよりも、関数内での演算時にコンストラクタが呼び出され、そちらはまともに内容の
方もコピーするので、間違いなく遅くなるぞ。

17 :
>>15
それでいいと思う。 最近のコンパイラって結構賢いし、あんまり細かいことは気にしない
方がいい。 stringも大抵はリファレンスカウント方式だから、コピーのコストは低い。
ちなみに、C++ではmake copying cheaperは一つの鉄則。 特に、STLユーザーは。
(Effective STL: Item 3参照) C++でもJavaみたいにプログラミングするのがいいみたい。
また、Efficent C++ Chapter 4. The Return Value Optimization参照するといい。

18 :
>>16,17
ほうほう、なるほど。
んじゃ、stringの場合、もう参照渡しにする理由なんてない
気がするなー。
サンクス。

19 :
>>18
Efficient C++でも読んでみるといいかも。

20 :
便乗して自分も質問・・・
vector< string > なんかを返却するのは危険だろうか?
例えばの用途としては、ファイル名を列挙するサービスを作ったとして、
その場合、なんらかの形で配列を返しますけど、安全性考えるなら
vectorだとか使いたいところ。それも、返却で。
アセンブリリスト出力させても、いまいち分からないし。
特にSTL内部の謎シンボルにcallだとかjumpされるともう追えない (;-;)

21 :
>>20
危険でも何でもない。std::vectorのコピーコンストラクタが全部面倒みてくれる。
もちろん参照を返す場合は、ローカル変数の参照を返してはいけない事などは
他のクラスやPODと同じ。

22 :
逆落とし穴?ってとこかな。有名だけど、一応足跡残しとこうかな。
vector<>ってアドレス的に連続性が保障されてるって、みなさん
知ってました?
例えば、vector< char > tbl;
とあって、tbl.size()==nな場合、
tbl[0]からtbl[n-1]は連続したアドレスであることが保障されてるんだってさ。STLの仕様として。
なので、char*p = &tbl[0] とかやって、その後は *p++ = 0x00 だとかで
アクセスできてしまうと。iteratorを使う必要もないみたい。
でもdequeでは、保障されてるのか、保障されてないのか不明。
明記された文書も見たこと無い。
listあたりだと確実に連続してないので、気にならないけど
dequeは気になるなー。

23 :
>>危険でも何でもない。std::vectorのコピーコンストラクタが全部面倒みてくれる。
あ、いや、おそらく20はそのコピーコンストラクタのコストを気にしてると
思うんですが。例えば、stringが100個くらいあったとして、それを返却すると
関数出口で発動するコピーコンストラクタがまじでコピーしているのか否か?など。

24 :
>>22
ISO/IEC14882:2003ではstd::vectorの連続性が保証されてるよ。
§23.2.4の当たり。
std::dequeは保証されてない。

25 :
ついでに調子こいて。
vector<bool>の時だけは特殊カスタマイズ発動して、ビット操作を意識した
コードが生成されるみたい。ビットセットなんかでメモリかつかつの時は
積極的にvector<bool>を使うといいみたい。

26 :
>>23
それだったら、std::vectorの実装による、としかいいようがない。
Copy On Writeなら、要素を変更しようとしない限り、管理情報のみを返すかもしれない。
const参照なら戻り値最適化をしてくれる可能性がさらに高まる。

27 :
vector<bool>は>>25の理由で正常にサイズが取得できないから使うな、と
聴いたことがあるんだけど…
ビットセットはそのままstd::bitsetじゃダメなん?

28 :
>>25
Effective STL第18項。
std::vector<bool>はコンテナの要件を満たしていない。
それよりもboost::dynamic_bitsetの方がまだましだろ。

29 :
stringだとか<<だとか使い始めて気になるといえば、
c言語時代のprintf可変調引数って便利だったなと。
%05dで0つめてくれたり。%08xで16進表記してくれたり。
.printf()というか.vsprintf()に相当するユーティってありそうだけど
見たことない。ご存知のかたいますー?

30 :
boost::format

31 :
>const参照なら戻り値最適化をしてくれる可能性がさらに高まる。
あー、そうかそうか。大抵の用途は確かに結果受け取りの
const参照で済むから、これからはこまめにconstつけてこうかな。


32 :
>>29
そんなに標準の書式マニピュレータを使うのが面倒か。
std::setw()以外は効果がストリームに残ったままになってしまうので元に戻すのが
面倒と言えば面倒だが。
直接フラグをsetf()/unsetf()でいじってもいいしな。

33 :
結構、みなさんboost使ってるんですね。
自分は使ったこと一度も無い。ものにもよると思うんだけど、
「これないと損するから黙って使え!」というくらい強烈なお奨め
boostモジュールってある?

34 :
boost::bind、boost::shared_ptr

35 :
boost::lambda (かなりコンパイラを選ぶ)

36 :
boost::regex
手軽に正規表現使うにはいい

37 :
boost::mplは基地外のためのライブラリかも。これで書かれたプログラムを見て
発狂しそうになった。

38 :
とりあえずスマートポインタを初めとして、
次期標準に入ることが決まってる物は使って損はないと思われ

39 :
template <int leftShifts_, int rightShifts_>
Int_ convert_(Int_ v) const {
return
boost::mpl::if_c <
(rightShifts_ == leftShifts_),
NoShiftPolicy,
boost::mpl::if_c <
(leftShifts_ > rightShifts_),
LeftShiftPolicy<leftShifts_-rightShifts_>,
RightShiftPolicy<rightShifts_-leftShifts_>
>::type
>::type::shift(v);
}
これ、c++のソースなんですか?
意味不明〜

40 :
>>次期標準に入ることが決まってる物は使って損はないと思われ
STLくらい標準であれば安心して使えるのだけどねー。
vc++、gccどちらでも同じソースをコンパイルできなければならない仕事を
抱えてるため。
でも次期標準決定してるなら、そろそろ馴れとかねば。

41 :
>>40
基本的にはSTLと同じ感覚で使えるので(かゆいところに手が届く感じ)
馴れるというより使ってみればすぐ分かるかと。
(spiritやlambdaは別として)
http://boost.sourceforge.net/regression-logs/
コンパイラの対応状況はここで見れば分かりますが、
VC7.1〜とgccならほぼ全て問題ないと思われ

42 :
>>20
関数で危険なのは、ポインタやレファレンスを返すとき。 特に、関数内部で作られたオブジェクトを
ポインタやレファレンスで返すのは重大な誤り。
この場合のオーバーヘッドが気になるなら、shared_ptrを用いるとよい。 shared_ptrを用いれば
Javaのようにオブジェクトを扱えるのでメモリ管理を気にしなくてよい。 ただし、構文が少し
煩雑になるという欠点を持つ。 *や->演算子を多用するから。
例:
typedef vector<string> VectorString;
typedef shared_ptr<VectorString> RefVectorString;
RefVectorString v(new VectorString());
v->push_back("Hello");
VectorString::iterator it = v->begin();
cout << *it;
RefVectorString f(){
RefVectorString v(new VectorString());
v->push_back("Hello");
return v;
}
RefVectorString v2 = f();
もOK!

43 :
鬱だ… STL

44 :
>>42
お前みたいな初心者が出てくると、かえって混乱するから引っ込んでてくれ。

45 :
>20 >42
基本的にはどこかで作ったvector<string>を「丸ごとコピーして」
値返しするんじゃない?
なので、スタックに積まれたvector<string>でも、戻り値は
「丸ごとコピーした」値なので、ローカルスコープを抜けて
元のvector<string>が削除されても戻り値に影響しないはず。
>26
そんな無茶なRVOあるの?
>33
boost::test
CPPUnitよりセットアップが簡単。ガリガリ手書きする必要あるけどね。

46 :
المغاربيعلىخالتاريخية
خصصةلمنالرأي كانتص
العاهلالمغربيلموريتانيا،ولكنل

47 :
>>15
boost-sandboxにあるBoost.Moveとかも見てみたら良いかも知れませんよ.
実体返しと構文一緒で効率は参照渡し並で(゚д゚)ウマーみたいな.
"Move"の概念がイマイチ浸透してないのとドキュメントが未整備なために
非常に取っ付きにくいライブラリですけれど.
>45
>boost::test
bjamのunit-testルールと組み合わせるとbjamと打つだけで
ビルド後即単体テストな感じでさらに(゚д゚)ウマー.

48 :
>>46
だったら、一辺OCAMLやHaskellかじってみたらジェネリックプログラミング
ってなにかよく理解できるとおもうよ

49 :
例えばboostのスマートポインタ(shared_ptr)を使うことにした場合、プロジェクト全体で使うように
統一する?
それとも複数箇所から参照されるようなオブジェクトだけにしておく?

50 :
統一しておかないと、そのうち同一プロジェクト内に複数の
リファレンスカウンティングスマートポインタが蔓延する罠。

51 :
その場合、生ポインタも禁止?

52 :
std::getline(std::basic_stringを引数に取るもの)も上限指定できないから、
無茶苦茶行数の長いテキストを読み込ませるなんて攻撃ができる。
なんて話をこの前C++スレで見かけたな。

53 :
見かけるもなにも有名な話なんで

54 :
今時basic_stringがCOWなSTLってあるんですか?

55 :
>>52-53
ぐぐっても見つからない。ほんとに有名な話なのか?

56 :
>>22
内部の実装はべた配列だ。だからといってメモリのアドレスを取得して(
そもそも iterator がポインタだったする)vector にアクセスするのは間違いだ。
社会に出たらやっていいことと悪いことを見分けなければならない。
内部のメモリポインタを取得して操作したいのなら valarray を使おう。
単純なループをまわすとき iterator を使おうが [] でアクセスしようが、
ポインタを使ったときと同じ速度のコードをはいてくれる。
今時ポインタのほうが速いというのはうそだと思っていいだろう
(もちろんレジスタの割り当てを気にするくらいの限界のチューニングをするときは違うかもしれないが)。
http://www.nantekotta.com/stl.html

57 :
std::stringの最後は\0とは限らないので、
終端の判別は必ずイテレータが終わりかどうかを調べること。

58 :
>>56
vector のメモリレイアウトはC言語の組み込み配列と同じだよ。
そんなメンテナンスされてない古い記事引用して何が言いたいの?

59 :
>>58
社会に出たらやっていいことと悪いことを見分けなければならない。

60 :
>59
うん、それは当然だと思うよ。で、その言葉にもう一言加えさせてくださいな。
「社会に出たらなぜそれをやって良いか/悪いかを人に納得できるように説明できなければならない」

61 :
>>57
イテレータ使ってループ回しておきながら 0 で終端判定ってのは中途半端な間違いだな。
そんな間違いする奴は string の要素に 0 が入ってたりしても嵌るんだろうな。

62 :
>>59
だったら>>56を間違いであると見分けることができないとな。

63 :
付けたしだけど、私もiteratorでアクセスできる場面でポインタを使うことにはもちろん反対ですよ。
CのAPIとデータをやりとりする際の一時バッファとして利用する場合などでは
ポインタによるアクセスは(vectorが空でなければ)安全で有用だという主張です。

64 :
ところで
>内部のメモリポインタを取得して操作したいのなら valarray を使おう。
これってOKだっけ?valarray自体カビの生えた前時代の遺物なので
どうでもいいっちゃどうでもいいんだけど

65 :
Cに渡すときはポインタと個数を渡すから
ベクタが空でも、個数も0なんで
結局安全なケースがほとんどだと思うが

66 :
>>65
C/C++使うんなら未定義動作に気をつける覚悟を持たなくちゃ。

67 :
普通定義してあるって・・・

68 :
>>67 未定義動作が何のことか知ってて言ってるのか?

69 :
>>68
うん。で、何が言いたいの?
まぁどうせ急に絡みたくなっただけなんだろうけど。

70 :
>>66はCの配列とvectorを比較しているわけではなく、
配列の先頭ポインタと個数を渡すというインターフェース自体が
危険だと言いたいのだろう。

71 :
>>69
ベクタが空な時はポインタ取っちゃダメ。

72 :
>>70
ちがうよ。なんでそうなる?

73 :
vectorが空の場合にしても、
Cの配列との比較にもvalarrayとの比較にもなってないから、
一体何を言いたいのだろうと考えた結果の結論でした。
失礼。
まだ>56の話かと思ってました。

74 :
void hoge(int n, int* ptr) {
 int i;
 for(i = 0; i < n; ++i) {
  hogehoge(ptr[n]);
 }
}
みたいな、Cルーチンに、
 hoge( &vect[0], vect.size() );
とかやると、本当にvectが空のときだけは動作が未定義なの?
そういう無駄なチェックを要求するう糞ったれな仕様は、やめてほしいんだが

75 :
hogeがそういう実装なら、nが0の場合はptrはアクセスされないんだから
未定義な訳がない気がするが
一般には、そういう「Cルーチン」の実装によるとしかいえないだろう

76 :
ちなみに、引数逆でしたな、すまん

77 :
俺が言いたいのは、Cルーチンのほうじゃなくて
vectorが空のときに、&vect[0]の動作が未定義かどうかってこと
ごみの値でいいから、とにかくポインタを返してさえくれる仕様ならば
何も文句は無い

78 :
>>74-77
vect.empty() ならば vect[0] の時点で未定義動作。
23.1.1/12 で a[n] は *(a.begin() + n) と定義されている。
23.1/7 により、 a.empty() のとき a.begin() == a.end() であり、
この値はコンテナに対する "past-the-end" と呼ばれる値になる。
24.1/5 により "past-the-end" は "dereferencable" ではない。
24.1.1/2 の票にイテレータ a に対する *a の事前条件として
a が "dereferencable" であることが挙げられている。
(ちなみに、 a が "dereferencable" であることは a + n を定義するための事前条件でもある。)
事前条件を満たさないことになるので、未定義動作となる。
(最後1行だけ、規格中に明らかな記述を見つけられなかった。17.4.3.7かな?)
ttp://www.kuzbass.ru/docs/isocpp/lib-containers.html#lib.sequence.reqmts
ttp://www.kuzbass.ru/docs/isocpp/lib-iterators.html#lib.iterator.requirements

79 :
>>74-77
http://pc2.2ch.net/tech/kako/1037/10377/1037795348.html
ここの 603-

80 :
どうやら、そうらしいな
まったくプログラム経験のろくに無い奴が作ったとしか思えん糞ったれな仕様だ
空の時には「未定義のポインタ値」を返すbegin_ptr()とかでもあればともかく
大体STLってのは、馬鹿丸出しの仕様が多い
vector<int> v;
vector<int>::const_itetator it;
 中略
copy(it, v.end(), dest);
こんなあたり前のコードが通らねえから、const_castがいるときた
const_iteratorを返す、cbegin(), cend()ぐらい用意しとけっての

81 :
>>80
名前は c_str() に倣って c_ptr() とかして
空のときはヌルでも返すことにするとよかったかもしれんな。
あと const_cast は要らんだろう、といちおう突っ込んでおく。

82 :
vector<int>::const_iterator v_end = v.end();
copy(it, v_end, dest);
とかやれば、要らんけど
やらんと、it は const_iterator で、v.end()はinteratorだから
templateでどっちの型を採用したらいいかわからんといって怒られる

83 :
つーかこの場合はconst_iteratorの存在理由こそ問題

84 :
const_iteratorは俺も嫌い
const_iterator ci = v.begin();
iterator i = const_cast< const_iterator >( ci );
これが出来る出来ないよりも、やって良い行為なのか
悪い行為なのかわかりづらい。
全てのiteratorでconst_castのオーバーロード関数を
用意してくれてると良かった。
つーか誰かSTLに詳しい人作ってYO!!STLport用でいいカエラ!

85 :
>>84
> iterator i = const_cast< const_iterator >( ci );
これ、何がしたいの?
> const_castのオーバーロード関数
これも意味がわからん。

86 :
>>85
const_cast< iterator >( ci );
これの間違い。
>> const_castのオーバーロード関数
>これも意味がわからん。
これこれ。
template< typename T0, typename T1 >
T1 const_cast( T0 );
template< >
std::set::iterator const_cast< std::set::const_iterator, std::set::iterator >( std::set::const_iterator i ){ ...}
これが全コンテナにあったら便利じゃん。

87 :
間違った、C++の仕様に合わせるとこうか。うぜー
template< typename T >
T const_cast( std::set::const_iterator i );
template< >
std::set::iterator const_cast< std::set::iterator >( std::set::const_iterator i ){ ...}

88 :
>>86-87
それがあると何がうれしいの?

89 :
iteratorをmutableメンバ変数として持たなければならない時とか

90 :
>>89
mutable iterator って宣言すればいいだけじゃない?
const_cast の出番がわからん。

91 :
>>90
class test
{
std::list<int> c;
mutable std::list<int>::iterator i;
void constant_method() const
{
i= c.begin(); // ERROR
i= const_cast< std::list<int>* >(&c)->begin(); // OK
}
void nonconstant_method()
{
i= c.begin(); // OK
}
};

92 :
>>91
それは普通の const_cast だろ。>>86-87 で言われてるものとは違うようだが。

93 :
>>92
説明不足ですまんね。
>>86-87があれば>>91みたいな方法とらずに
いいなーみたいな。それだけ。
まあ、あったらイイナ♪(マイメロふう)程度のもの
なんで真に必要な場面はそうないんじゃない?

94 :
こういうのがあればいいんだろ
template <class InputIterator1, class InputIterator 2, class OutputIterator>
void copy(InputIterator1 first, Inputiterator2 last, OutputIterator out);
>>86-87みたいなのは、あってもいいけど
紛らわしいから名前を変えた方がいいよね。
const_cast とはやってることが全然違うし。
const_iterator_cast とかなんとか。

95 :
そういうのがあると、beginとendの型が全く違っててもパラメタがマッチするから
バグの原因だよなあ
結局解決策は、iteratorの種類ごとにcopyの多重定義ぐらいしかなさげ

96 :
>>95
std::iterator_traits + boost::enable_if
あたりを使えばなんとなるっしょ。
どのくらい制限すればいいかは、議論の余地が大有りだろうけど。

97 :
>>95
>そういうのがあると、beginとendの型が全く違っててもパラメタがマッチするから
インスタンス化で弾かれるだろ。
俺は>>94が正解だと思う。
http://www.boost.org/libs/iterator/doc/new-iter-concepts.html
で提案されてるInteroperableIteratorってのもそのためのものだよな?

98 :
インスタンス化による拒否だと実装の深い部分で弾かれるのが若干嫌なので
interoperable iterator + enable_if(もしくはconcept check)というのが最良ですかね.
後,最近iterator, const_iterator間のinteroperability以外に
iterationの情報をiteratorの型にエンコードしてコンパイル時にalgorithmをアンロールする
とか,それと普通のiteratorによるループを融合しようという趣旨の話もちらほらあるので
(Fusionとか最近David Abrahamsがやっているヤツとか)
その関連でも>>94の形が今後ちらほら出てくるかも知れないです.

99 :
void foo(const string& cstr) {
 string str = cstr;
 ・・・
 copy(str.begin(), cstr.end(), dest);
こんなエラーが検出できなくなるのが、ちと気になる。

100read 1read
1read 100read
TOP カテ一覧 スレ一覧 2ch元 削除依頼
推薦図書/必読書のためのスレッド 69 (201)
Java低速GUI Swing & JavaFX 10 (376)
【肥大化】C++ を見捨てたヤシ 2人目【複雑化】 (926)
プログラム板 自治スレッド Part5 (821)
36歳のオッサンがC言語を始めたいのだが・・・ (969)
【注意】STLの落とし穴【危険】 (918)
--log9.info------------------
最近ちょっと疲れ気味の俺におすすめする音楽スレ (219)
【現音?】D.F.O.のスレ【イージーリスニング?】 (254)
眠れない夜に聴く曲。 (207)
幻想的な情景・状況を頭に連想させる音楽 (227)
加羽沢美濃さんを応援するスレッド (339)
新垣勉さんのお部屋 (358)
 ビフォーアフター 松谷卓  (311)
ギターデュオDEPAPEPEはゴンチチのパクリ!! (312)
車のCMの使用音楽ってよくね? (228)
最強サブリミナル『グリーンエナジー』効く(゜゜) (232)
Josh Groban について【3rdAWAKE】 (249)
あんたたちから見たパンク/ヘヴィメタル (216)
新星★マイア・バルー★ワールドミュージックの歌姫 (407)
■カントリースレ@ワールド板 3■ (376)
葉加瀬太郎! (265)
フラメンコ大好き 2 (802)
--log55.com------------------
JPFFについて語ろう
関西学生アメリカンフットボール Part99
関西学生アメリカンフットボール Part100
関西学生アメリカンフットボール Part96
関西学生アメリカンフットボール 一部総合
関西学生アメリカンフットボール Part98
関東学生アメリカンフットボール エリア総合
関西学生アメリカンフットボール Part97