Hatena::ブログ(Diary)

joynote break;

2011-06-08

[][][] ムーブセマンティクス(Move Semantics)はテクニックにすぎない

正確には、C++0xではムーブセマンティクスを実現"しやすく"なっただけで、

ムーブ自体は昔からのテクニックに過ぎない。

という事をやっと理解したっぽい?メモ。

#include <iostream>
#include <vector>
#include <string>

std::vector<std::string> add_bar(std::vector<std::string> lhs)
{
  lhs.push_back("bar");
  return lhs;
}

int main(){
  const size_t v_size = 100000;
  std::vector<std::string> v1;
  std::vector<std::string> v2;

  v1.reserve(v_size*2);
  for(size_t i=0; i < v_size; ++i){
    v1.push_back("foo");
  }

  // クソみたいに重いコピーが起こる
  v1 = add_bar(v1);

  // moveされてすぐ終わる
  v1 = add_bar( std::move(v1) );

  // この時点でv1には100000個の"foo"と2個の"bar"が入っている。

  // moveする。つまり、"ムーブコンストラクタ(or代入演算子)を呼び出す"。
  v2 = std::move(v1);

  // この時点でv1には何も入っていない。
  // この時点でv2には100000個の"foo"と2個の"bar"が入っている。

  // そしてその処理は、std::vectorの中身を書いた人がポインタのつなぎ替え等で"実装したもの"である。

  return 0;
}

============================

Move Semanticsが???となる人の突っかかりポイントは(自分がまさにそうだったが)、新しく特別なメモリ上の操作が自動、あるいは新しい構文で行われるんじゃないと思っている所。

実際は単に、移動してよい場合というのを区別できるムーブコンストラクタやムーブ代入演算子が定義されただけで、中の実装は単なるポインタの操作である。(だから配列ポインタ等を用いず生で使ってある場合にはコピー==ムーブ)

実際にはSTLの内部処理とかに使われていて、一般のプログラマが気にする事ではないけれども、どういう事が行われているかを知っているのと知らないのではだいぶ違うよね的な。

2011-05-20

[] gcc 4.5.3を使うまで:健忘録

はじめてgccビルドったのでメモ。

未来の自分 or gccC++0x使ってみたいだけの人用。

shに書いたらそのまま通るノリで手順書いてみる。

http://www29.atwiki.jp/akcnv/pages/28.html

ありがたいこのページを参考にする感じ。

環境はUbuntu10.04 EeePC 1000HE。

homeにインストールするのは正道ではない気はするので、そのうち直そう。うん。


user名=joyとする

cd
wget http://ftp.gnu.org/gnu/gcc/gcc-4.5.3/gcc-4.5.3.tar.gz
wget http://ftp.gnu.org/gnu/gmp/gmp-5.0.2.tar.gz
wget http://ftp.gnu.org/gnu/mpfr/mpfr-3.0.1.tar.gz
wget http://www.multiprecision.org/mpc/download/mpc-0.8.2.tar.gz
tar zxvf gcc-4.5.3.tar.gz
tar zxvf gmp-5.0.2.tar.gz
tar zxvf mpfr-3.0.1.tar.gz
tar zxvf mpc-0.8.2.tar.gz
mkdir gcc
mv gmp-5.0.2 gcc/gmp
mv mpfr-3.0.1 gcc/mpfr
mv mpc-0.8.2 gcc/mpc
cd gcc
cp -r gmp ../gcc-4.5.3/
cp -r mpfr ../gcc-4.5.3/
cp -r mpc ../gcc-4.5.3/
../gcc-4.5.3/configure --prefix=$HOME/usr --enable-bootstrap --enable-shared \
--enable-static --enable-shared-libgcc --enable-_cxa_atexit --with-dwarf2 \
--disable-sjlj-exceptions --enable-languages=c,c++
sudo make
sudo make install
cd
echo "PATH=/home/joy/usr/bin:$PATH" >> .bashrc
rm -f gcc-4.5.3.tar.gz
rm -f gmp-5.0.2.tar.gz
rm -f mpfr-3.0.1.tar.gz
rm -f mpc-0.8.2.tar.gz
sudo rm -rf gcc-4.5.3
sudo rm -rf gcc
sudo rm -rf gmp
sudo rm -rf mpfr
sudo rm -rf mpc

たぶんコレで再起動したらいいハズ。いいPC使ってればmake -j4とかが早そう。

無駄な箇所とかありそうだけど、気力尽きたので調べるのはまた今度。

メインPCのVM上でこれで通るか試してみよう。

もちろんご利用は計画的に

これをsh化して実行して、たとえOS吹っ飛んでも自己責任で。一応。

:------------------------:

追記:

アップデートして最新(2011/05/21現在)にしないと怒られたが、最新にしたら通った

:------------------------:

追記[2011/06/05]:

CentOSでやったら途中で怒られた。

sudoコマンドがデフォで存在しない and rootじゃないとデフォsudo使えないってさ。

よって、「yum install sudo」 と、ルートになってから「visudo」で「# User privilege specificationの所に自分のユーザー名追加で無事ビルドできた

2011-02-05

[][] ローカルな型とか無名型をテンプレート引数として使う

ローカルな型とか無名型をテンプレート引数として使える。

・・・オブジェクト生成時ってどうするんだろ? とりあえず動くもの。

  • ローカルな型
#include <vector>
#include <iostream>
using namespace std;
 
int main(){
  struct data{
    int a,b,c;
  } object = {1,2,3};
  vector<data> v;
  v.push_back(object);
  cout << v[0].a;
}

出力結果

1
  • 無名型
#include <vector>
#include <iostream>
using namespace std;
 
int main(){
  // 無名な型のインスタンスを生成
  struct{
    int a,b,c;
  } object = {1,2,3};
  // decltypeで型を取得してvector生成して突っ込む
  vector<decltype(object)> v;
  v.push_back(object);
  cout << v[0].a;
}

出力結果

1

2011-01-21

[][] 前回のカスタムデリータ使うときの話で

http://d.hatena.ne.jp/gintenlabo/20110121/1295626957にstd::functionの型消しの影響で最適化かかりにくいし、実行効率・メモリ効率も悪いよね的な事が書かれていたので、これは実測して試してみるしかないネという事なのでした。

#include <iostream>
#include <memory>
#include <functional>
#include <boost/timer.hpp>
using namespace std;

template<class T, class D>
std::unique_ptr<T, D> make_unique_ptr( T* p, D d ) {
  return std::unique_ptr<T, D>( p, std::forward<D>(d) );
}

class Hoge{
public:
  static int c_count;
  static int d_count;
  Hoge():n(0){++c_count;}
  ~Hoge(){}
private:
  int n;
};

int Hoge::c_count = 0;
int Hoge::d_count = 0;

int main(){
  boost::timer t;

  t.restart();
  for(size_t i=0; i < 1000000U; ++i){  
    auto hoge = make_unique_ptr(new Hoge(),[](Hoge* hoge){Hoge::d_count++; delete hoge;});
  }
  cout << Hoge::c_count << endl;
  cout << Hoge::d_count << endl;
  cout << t.elapsed() << "秒かかりました" << endl;

  t.restart();
  for(size_t i=0; i < 1000000U; ++i){
    std::unique_ptr<Hoge,std::function<void(Hoge*)>> test(new Hoge(),[](Hoge* fuga){Hoge::d_count++; delete fuga;});
  }
  cout << Hoge::c_count << endl;
  cout << Hoge::d_count << endl;
  cout << t.elapsed() << "秒かかりました" << endl;
}

出力結果

1000000
1000000
0.187秒かかりました
2000000
2000000
0.245秒かかりました

うちの環境ではだいたい1.2〜1.8倍くらい時間かかるっぽい。

……って、えらく違うなコレ。マジか。

  • - - - - -

コメントで指摘貰ったので若干修正

  • - - - - -

2011/10/04

std::functionが例外を投げた場合にリークするとの指摘

これは……std::shared_ptr最強説か……(ただし無駄コスト発生)

変にトリッキーな事をするものじゃないということですかのう

2011-01-09

[][] lambda式をもっと刺激的に使う

面白い話を小耳に挟んだのでメモメモ

#include <iostream>
#include <vector>
#include <functional>
using namespace std;
 
int main(){
  vector<vector<int>> array;
  vector<int> tempA = {1,2,3,4,5,6,7,8,9,10};
  array.push_back( tempA );
  vector<int> tempB = {11,12,13,14,15,16,17,18,19,20};
  array.push_back( tempB );
 
  [&]{
    for(size_t i=0; i < array.size(); ++i){
      for(size_t j=0; j < array[i].size(); ++j){
        if( array[i][j] == 17 )return;
        cout << array[i][j] << endl;
      }
    }
  }();
  return 0;
}

出力結果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

これがC++0xにおける多重ループからの脱出方法だっ!(キリッ

終了フラグ作ってbreak;break;するくらいなら俺はreturnするぞジョジョォー!!

って話。

もちろん遅(ry