最終更新日時(UTC): 2026年06月09日 03時37分47秒
Akira Takahashi が更新

履歴 編集

function
<atomic>

# std::atomic_ref::address(C++26)

    constexpr address-return-type address() const noexcept;

## 概要

参照しているオブジェクトのアドレスを取得する。

戻り値型`address-return-type`は説明専用の型エイリアスであり、`COPYCV(T, void)*`と定義される。`COPYCV(T, void)`は、`T`の最上位のCV修飾を`void`に付加した型である。たとえば`atomic_ref<int>`では`void*`、`atomic_ref<const int>`では`const void*`を返す。

### この関数を必要とする状況

#### データ構造の要素へのアトミックアクセス

配列の各要素にアトミックアクセスする際、`std::atomic`オブジェクトでは以下のように記述する。

    std::array<std::atomic<int>, N> array;

    int fetch_add_idx(std::atomic<int>* base, size_t i, int value) {
      return base[i].fetch_add(value);
    }

これを`std::atomic_ref`で記述する場合、アトミック性をもたせてアクセスするには、この関数を使用して以下のようにする。

    int fetch_add_idx(std::atomic_ref<int> base, size_t i, int value) {
      int* p = static_cast<int*>(base.address());
      return std::atomic_ref{*(p+i)}.fetch_add(value);
    }

#### 必要なときにのみアトミックアクセスする

以下の例では、複数のスレッドが並行にメモリにアクセスし、カウンタをインクリメントすることでアクセス終了を知らせている。最後のスレッドはメモリにアクセスする余分な処理を実行する必要があるが、並行にメモリにアクセスするほかのスレッドがないため、これらのアクセスはアトミックである必要がない：

    void thread(atomic_ref<int>* data, atomic_ref<int> counter, int nthreads) {
      data->fetch_add(42, memory_order_relaxed);
      int* d = static_cast<int*>(data->address()); // dataへの生ポインタを取得
      data->~atomic_ref();            // このスレッドのデータへのatomic_refを破棄する
      int pos = counter.fetch_add(1); // データの破棄が完了したことを伝える
      if (pos != (nthreads - 1))
        return;

      // 最後のスレッド: アトミックアクセスする必要がない
      int last_data = *d; // 非アトミックアクセス
      // …
    }

## 戻り値

`*this`が参照するオブジェクトを指すポインタを、`T`のCV修飾を引き継いだ`void`へのポインタとして返す

## 例外

投げない

## 備考

- 戻り値を`void`へのポインタとしているのは、参照先オブジェクトへの不用意なアクセスという誤用を防ぐためである
  - この関数の主な用途は、ポインタ値そのもの（ハッシュ化や比較、配列のインデックス計算など）を使うことである。一方、参照先のオブジェクトを直接読み書きすると、同じオブジェクトを参照するほかの`atomic_ref`が生存している状況ではデータ競合となる
  - 仮に戻り値が`T*`であれば、以下のように参照先を何気なく読み書きでき、アトミック性を壊すアクセスに気付きにくいという問題が起きる：

        int x = 0;
        std::atomic_ref ar{x};

        int* p = ar.address(); // 戻り値型がT*であった場合、
        int value = *p;        // 非アトミックな間接参照が容易にできてしまう

## 例

    #include <iostream>
    #include <atomic>
    #include <thread>

    void f(std::atomic_ref<int> ar, int i) {
      int* p = static_cast<int*>(ar.address());
      std::atomic_ref{*(p + i)}.fetch_add(1);
    }

    int main()
    {
      int ar[3] = {};

      std::thread t1{[&ar]{ f(std::atomic_ref{ar[0]}, 1); }};
      std::thread t2{[&ar]{ f(std::atomic_ref{ar[0]}, 1); }};

      t1.join();
      t2.join();

      std::cout << ar[1] << std::endl;
    }

### 出力

    2

## バージョン

### 言語

- C++26

### 処理系

- Clang: 19.0 ❌
- GCC: 14 ❌
- Visual C++: ??

## 参照

- P2835R7 Expose `std::atomic_ref` 's object address
- P3936R1 Safer `atomic_ref::address` (FR-030-310)
  - C++26で、誤用を防ぐために戻り値型を`T*`から`COPYCV(T, void)*`（`T`のCV修飾を引き継いだ`void`へのポインタ）に変更した