yohhoyの日記

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

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

C++20 requires式(requires-expression)の単純な利用では非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