メモの日々


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されていることが分かる。