メモの日々


2016年03月11日(金) [長年日記]

[c++] throw e と throw の違い

C++でcatchした例外を再throwしたいとき、次の2通りの書き方に違いがあるのか?

    } catch (const exception& e) {
      throw e;
    }
    } catch (const exception& e) {
      throw;
    }

ある。前者の場合はexceptionオブジェクトのコピーが投げられるが、後者はcatchした例外オブジェクトがそのまま投げられる。

これにより、catchするクラスにサブクラスがある場合に前者の書き方をすると基底クラスのインスタンスが投げられることになる。

#include <iostream>

struct base {
  base() {}
  base(const base& rhs) { std::cout << "base: copied" << std::endl; }
  base(base&& rhs) = delete;
};

struct sub : public base {
  sub() {}
  sub(const sub& rhs) { std::cout << "sub: copied" << std::endl; }
  sub(sub&& rhs) { std::cout << "sub: moved" << std::endl; }
};

int main() {
  try {
    try {
      throw sub{};
    } catch (const base& e) {
      // eを投げる
      std::cout << "throw e" << std::endl;
      throw e;
    }
  } catch (const sub& e) {
    std::cout << "caught sub" << std::endl;
  } catch (const base& e) {
    std::cout << "caught base" << std::endl;
  }

  std::cout << "-----" << std::endl;

  try {
    try {
      throw sub{};
    } catch (const base& e) {
      // throwだけ
      std::cout << "throw" << std::endl;
      throw;
    }
  } catch (const sub& e) {
    std::cout << "caught sub" << std::endl;
  } catch (const base& e) {
    std::cout << "caught base" << std::endl;
  }
}
throw e
base: copied
caught base
-----
throw
caught sub

元々throwしているのはクラスsubのインスタンスだが、「throw e」とした場合はbaseのコピーコンストラクタが呼ばれクラスbaseのインスタンスが投げられている。「throw」とした場合は元々のsubオブジェクトが再throwされていることが分かる。


2016年03月12日(土) [長年日記]

[c++] C++で非復元抽出

集合からN個の要素をランダムに取り出して部分集合を作りたい。現状ではC++の標準ライブラリにはそのような関数が無いみたい。Boostにも見当たらない。

<experimental/algorithm>というヘッダがあればstd::experimental::sample()が使え、GCC 5やClangだと使えそう。が、我が環境のGCC 4.8では使えない。

で、GNUのlibstdc++にはSGI Extensionsというライブラリが含まれていて、<ext/algorithm>をインクルードすると __gnu_cxx::random_sample(), __gnu_cxx::random_sample_n() という関数を使えるようになる。使い方はSGIのサイトのマニュアルで分かる。

random_sample()は抽出元コンテナ内での順番を維持しないが、random_sample_n()は維持する。それぞれ、Knuthの"Algorithm R"、"Algorithm S"というアルゴリズムを使っているみたい。

#include <iostream>
#include <numeric>
#include <vector>
#include <ext/algorithm>

int main() {
  std::vector<int> src(20);
  std::iota(src.begin(), src.end(), 0);

  std::vector<int> dst1(10);
  __gnu_cxx::random_sample(src.begin(), src.end(), dst1.begin(), dst1.end());

  std::vector<int> dst2(10);
  __gnu_cxx::random_sample_n(src.begin(), src.end(), dst2.begin(), dst2.size());

  for (auto v : dst1) std::cout << v << " ";
  std::cout << std::endl;
  for (auto v : dst2) std::cout << v << " ";
  std::cout << std::endl;
}
0 19 18 16 4 13 10 7 14 9
0 3 4 5 8 9 10 13 15 19

ライブラリを使わず自分で実装する場合は

が参考になる。