最終更新日時(UTC):
が更新

履歴 編集

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が参照するオブジェクトを指すポインタを、TCV修飾を引き継いだ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

処理系

参照