yohhoyの日記

技術的メモをしていきたい日記

James Bond in C++ Standard

プログラミング言語C++標準規格の索引(Index)に紛れ込むジェームズ・ボンド

[expr.prim.lambda] Add index entry for example of *this capture.

https://github.com/cplusplus/draft/commit/703d892264af814a64140b17ffe2bf6ae9274dde

関連URL

OpenMP 5.1仕様リリース

2020年11月 OpenMP 5.1仕様リリース記事 OpenMP ARB releases OpenMP 5.1 with Vital Usability Enhancements より抄訳。

OpenMP仕様バージョン5.1はOpenMP ARB、主要なコンピュータハードウェア/ソフトウェアベンダのグループ、そしてOpenMPコミュニティのユーザによって共同開発されました。改訂仕様は下記の主要な機能追加を含みます:

アクセラレータデバイス相互作用の改善:

コンパイラ最適化の改善を可能とする多くの情報提供:

  • assumeディレクティブによってプログラムのOpenMP利用方法に関する追加情報を処理系へと指示でき、コンパイラによって安全に適用できる追加の最適化や、安全なOpenMPサブセット実装が可能となります。*2
  • データ局所性を向上させるtileディレクティブから始まり、ループを完全または部分的に展開するunrollディレクティブと、ループ変形ディレクティブが追加されます。

スレッド実行のフィルタリング:

  • maskedディレクティブによりコード区間の実行をスレッド・サブセットに制限できます。

ユーザによるコンパイル時エラー・警告生成の許可:

  • 新しいerrorディレクティブが追加されます。

モダンC++におけるOpenMP利用の改善:

  • 旧来のpragma形式に代わって、OpenMPディレクティブ指定にC++属性構文を利用できるようになり、テンプレートとの統合がシンプルになります。*3
  • C11, C18, C++11, C++14, C++17, C++20のフルサポート:これらの言語で書かれたプログラムはOpenMP並列化可能です。

Fortranサポートの改善:

  • Fortran 2008が完全にポートされ、Fortran 2018への初期サポートが追加されます。

環境固有な関数宣言の簡易化:

  • 単一declare variantコンストラクトに複数関数を宣言可能となり、コードを特定環境により一層合わせられます。

関連URL

*1: (PDF)Additional Definitions (v2.0)によれば次の外部ランタイム環境が定義される:NVIDIA CUDA, Khronos OpenCL, Khronos SYCL, AMD ROCm HIP, Intel oneAPI Level Zero

*2:訳注:“対象コード区間入れ子OpenMPディレクティブを含まない” といった最適化ヒント情報を表明できる。

*3:訳注:名前空間 omp 以下に directive と sequence の2属性が追加される。for構文に omp parallel と omp for ディレクティブを指定する例:[[omp::sequence( directive(parallel), directive(for) )]] for (/*...*/) {} 積極的に使いたくなる構文ではない('A`)...

制約式std::bool_constant<cond>::value

C++20 制約式(constraint-expression)でテンプレートパラメータに依存する定数条件式condを表現する場合、非定数式に起因するハードエラーを防ぐためstd::bool_constant<cond>::valueと記述する必要がある。

#include <type_traits>

struct X {
  // X::valueは非定数式
  static inline int value = 42;
};
struct Y {
  // Y::valueは定数式
  static constexpr inline int value = 42;
};
struct Z {};

template<typename T>
concept C0 = (T::value == 42);
static_assert(!C0<X>);  // NG: ill-formed
static_assert( C0<Y>);  // OK
static_assert(!C0<Z>);  // OK

template<typename T>
concept C1 = std::bool_constant<T::value == 42>::value;
static_assert(!C1<X>);  // OK
static_assert( C1<Y>);  // OK
static_assert(!C1<Z>);  // OK

C++標準ライブラリではuniform_random_bit_generatorコンセプトで同テクニックが利用される。C++20 26.6.2.3/p1より引用。

A uniform random bit generator g of type G is a function object returning unsigned integer values such that each value in the range of possible results has (ideally) equal probability of being returned. [Note: The degree to which g's results approximate the ideal is often determined statistically. --end note]

template<class G>
  concept uniform_random_bit_generator =
    invocable<G&> && unsigned_integral<invoke_result_t<G&>> &&
    requires {
      { G::min() } -> same_as<invoke_result_t<G&>>;
      { G::max() } -> same_as<invoke_result_t<G&>>;
      requires bool_constant<(G::min() < G::max())>::value;
    };

関連URL

2進数リテラル in 標準C

プログラミング言語Cの次期仕様C2xでは 2進数リテラル(binary literal) が正式仕様となる。そこ今更とか言わない。

// C2x
unsigned x = 0b101010;
unsigned y = 0B11110000;

ノート:2003年時点の (PDF) Rationale for International Standard Programming Languages Cでは下記の通り否定的だったが、C++14で2進数リテラルが導入されたことも影響していそう。

6.4.4.1 Integer constants
A proposal to add binary constants was rejected due to lack of precedent and insufficient utility.

関連URL

std::is_convertible vs. std::convertible_to

C++標準ライブラリstd::is_convertibleメタ関数とstd::convertible_toコンセプトの超微妙な違い。本記事の内容はStackOverflowで見つけた質問と回答に基づく。

要約:

  • is_convertible<From, To>メタ関数:From型からTo型へ暗黙変換できることを検査する。
  • convertible_to<From, To>コンセプト:From型からTo型へ暗黙変換および明示変換できることを検査する。

暗黙変換は可能だが明示変換が許可されないケースで差異が生じる。ジェネリックライブラリの設計者大変すぎでしょ。

#include <concepts>
#include <type_traits>

struct From;
struct To {
  To() = default;
  // From→Toの明示変換を禁止
  explicit To(From) = delete;
};
struct From {
  // From→Toの暗黙変換は許可
  operator To() { return {}; }
};

static_assert( std::is_convertible_v<From, To> );
static_assert( !std::convertible_to<From, To> );

To to1 = From{};  // OK: 暗黙変換
To to2{From{}};   // NG: 明示変換

C++20(N4861) 18.4.4/p1, 20.15.6/p5より引用(下線部は強調)。

Given types From and To and an expression E such that decltype((E)) is add_rvalue_reference_t<From>, convertible_to<From, To> requires E to be both implicitly and explicitly convertible to type To. The implicit and explicit conversions are required to produce equal results.

template<class From, class To>
  concept convertible_to =
    is_convertible_v<From, To> &&
    requires(add_rvalue_reference_t<From> (&f)()) {
      static_cast<To>(f());
    };

The predicate condition for a template specialization is_convertible<From, To> shall be satisfied if and only if the return expression in the following code would be well-formed, including any implicit conversions to the return type of the function:

To test() {
  return declval<From>();
}

[Note: This requirement gives well-defined results for reference types, void types, array types, and function types. -- end note] Access checking is performed in a context unrelated to To and From. Only the validity of the immediate context of the expression of the return statement (8.7.3) (including initialization of the returned object or reference) is considered. [Note: The initialization can result in side effects such as the instantiation of class template specializations and function template specializations, the generation of implicitly-defined functions, and so on. Such side effects are not in the "immediate context" and can result in the program being ill-formed. -- end note]

関連URL

非staticデータメンバを判定する制約式

C++20 requires式の単純な利用では非static/staticメンバを区別できない。requires式の本体部は評価されない(unevaluated)ため、通常コードとは異なる規則が適用されることに注意。

// staticメンバmを持つ型X
struct X {
  static const int m = 1;
};

// (非static)メンバmを持つ型Y
struct Y {
  int m = 2;
};

// staticメンバT::mを確認するコンセプト(?)
template <typename T>
concept HasStaticM = requires {
    { T::m } -> std::convertible_to<int>;
  };

// 式 T::m はmがstaticメンバのときのみ有効
assert( X::m == 1 );  // OK
static_assert( HasStaticM<X> );  // OK

// mが非staticメンバの場合は式 T::m と書けないが...
assert( Y::m == 2 );  // NG: ill-formed
static_assert( HasStaticM<Y> );  // OK !?

T::mが非static/staticデータメンバのいずれかを判定するには、&T::mの型をstd::is_member_object_pointerメタ関数に通す。

  • X::mはstaticデータメンバのため、式&X::mは通常のポインタ型(int*)となる。
  • Y::mは非staticデータメンバのため、式&Y::mはデータメンバへのポインタ型(int X::*)となる。
template <typename T>
concept HasStaticM = requires {
    { T::m } -> std::convertible_to<int>;
    requires !std::is_member_object_pointer_v<decltype(&T::m)>;
  }; 

static_assert(  HasStaticM<X> );  // OK
static_assert( !HasStaticM<Y> );  // OK

C++20(N4861) 7.5.4/p2, 7.5.7/p2より引用(下線部は強調)。

id-expression:
   unqualified-id
   qualified-id


2 An id-expression that denotes a non-static data member or non-static member function of a class can only be used:

  • as part of a class member access (7.6.1.4) in which the object expression refers to the member's class or a class derived from that class, or
  • to form a pointer to member (7.6.2.1), or
  • if that id-expression denotes a non-static data member and it appears in an unevaluated operand. [Example:
struct S {
  int m;
};
int i = sizeof(S::m); // OK
int j = sizeof(S::m + 42); // OK

-- end example]

2 A requires-expression is a prvalue of type bool whose value is described below. Expressions appearing within a requirement-body are unevaluated operands (7.2).

関連URL