最終更新日時: 2024年11月18日 14時36分09秒 (JST)
Koichi Murase が更新

履歴 編集

function
<thread>

# std::thread::コンストラクタ(C++11)

    thread() noexcept;                      // (1)

    template <class F, class ...Args>
    explicit thread(F&& f, Args&&... args); // (2)

    thread(const thread&) = delete;         // (3)
    thread(thread&&) noexcept;              // (4)

## 概要

- (1) : デフォルトコンストラクタ。新しいスレッドを生成せず、空の状態にする。
- (2) : 新しいスレッドを生成し、そのスレッド上で引数`args...`を渡して、関数オブジェクト`f`を呼び出す。
- (3) : コピーコンストラクタ。コピー不可。
- (4) : ムーブコンストラクタ。スレッドの所有権を移動する。

## 要件

- (2) : 型`F`および`Args`に含まれるすべての型`Ti`はムーブコンストラクト可能な型でなければならない。また、`INVOKE(DECAY_COPY(std::forward<F>(f)), DECAY_COPY(std::forward<Args>(args))...)`が有効な式でなければならない。

## 効果

- (2) : 新しいスレッドを生成し、`INVOKE(DECAY_COPY(std::forward<F>(f)), DECAY_COPY(std::forward<Args>(args))...)`を実行する。ただし`DECAY_COPY`は同コンストラクタを呼び出したスレッド上にて評価される。また`f`のコピーの戻り値は無視される。

  - `DECAY_COPY(x)`は `template <class T> typename std::decay<T>::type decay_copy(T&& v) { return std::forward<T>(v); }` と定義される。おおよそ、`x`が配列型なら先頭要素へのポインタ、`x`が関数型ならその関数ポインタ、`x`がコピーコンストラクト可能な型なら`x`からコピーされたオブジェクト、`x`がムーブコンストラクト可能な型なら`x`からムーブされたオブジェクトとなる。

  - `INVOKE(f, arg...)`は`f`が関数オブジェクトならば `f(arg...)` 形式の関数呼び出しとなる。詳細はINVOKEの定義参照。 もし`INVOKE(DECAY_COPY(std::forward<F>(f)), DECAY_COPY(std::forward<Args>(args))...)`呼び出しからcatchされない例外が送出された場合、`std::terminate()`が呼び出されてプログラムは異常終了する。

## 同期操作

- (2) : 同コンストラクタの呼び出し完了は、fのコピーの呼び出し開始に対して同期する。つまり、「コンストラクタ呼び出し側スレッドT0でのコンストラクタ呼び出し完了」は、「新しいスレッド`T1`上での`f`のコピーの呼び出し開始」よりも前に発生する。

## 事後条件

- (1) : `get_id() == id()`。
- (2) : `get_id() != id()`。`*this`は新しいスレッドと関連付けられる。
- (4) : ムーブ前の`x.get_id() == get_id()` かつ ムーブ後の`x.get_id() == id()`

## 例外

- (2) : 新しいスレッドの作成に失敗した場合、`system_error` 例外を投げる。その例外オブジェクトには、以下のエラー状態が設定されうる：

  - `resource_unavailable_try_again` : 新たなスレッドを作るためのリソースが不足している。もしくはシステムやプロセスが規定するスレッド数の上限を超過した。

## 備考

- (2) :
  - C++14 : `std::remove_cvref<F>::type`が`std::thread`型である場合、この関数はオーバーロード解決に参加しない。

## 例

    #include <memory>
    #include <thread>
    #include <utility>
    #include <cassert>

    int func(int v, int& ri, std::shared_ptr<int> sp, std::unique_ptr<int> up)
    {
      // spはコピーされた値が、upはムーブされた値が渡されてくる
      v = ri = 42;

      int x = *sp + *up;
      assert(x == 7);
      return x;  // この戻り値は無視される
    }

    int main()
    {
      int i1 = 0;
      int i2;
      std::shared_ptr<int> sp0 = std::make_shared<int>(5);
      std::unique_ptr<int> up0(new int(2));

      std::thread t( func, i1,  std::ref(i2), sp0, std::move(up0) );
      // ...
      t.join();

      assert(i1 == 0 && i2 == 42);

      return 0;
    }

### 出力

## バージョン

### 言語

- C++11

### 処理系

- Clang:
- GCC: 4.6.3 ✅, 4.7.0 ✅
- ICC:
- Visual C++: 2012 ✅, 2013 ✅, 2015 ✅
  - 2012, 2013は、(2)での実引数の受け渡しにムーブを使用しない問題がある。上記の例でも、`std::unique_ptr<int>`の実引数でコンパイルエラーになる。
  - 2012はコピーコンストラクタのdeleteに対応していないため、代わりにprivateで宣言のみ行う手法で代用されている。

## 参照

- LWG Issue 2097. `packaged_task` constructors should be constrained
- LWG Issue 3039. Unnecessary `decay` in `thread` and `packaged_task`