Search code examples
c++templatesc++20c++-conceptsc++-templates

Understanding concepts. Check if a member is static


Lets say we have a simple concept like:

template <typename T>
concept MyConcept = requires {
    T::value == 42; 
};

In my understanding the concept says that, if the code T::value == 42 is valid for the type T, I pass. So the value MUST be a static member, right?

I have a struct

struct Test { int value = 0; }

and the next template function

template <MyConcept T>
void call() { /*...*/ }

and when I try to do this:

int main()
{
    call<Test>();
}

It works!

And the question is: why does it work? Test::value == 42 is not a valid code for the type Test.

I found a method to fix it like:

template <typename T>
concept MyConcept = requires {
    *(&T::value) == 42; 
};

And it "works" as expected:

<source>:6:20: note: the required expression '((* & T::value) == 42)' is invalid

And this concept works for the static value only, as it should be. But why does the T::value == 42 work?

godbolt: https://godbolt.org/z/d3GPETEq9

UPD: + example https://godbolt.org/z/d8qfzK9b6


Solution

  • And this concept works for the static value only, as it should be. But why does the T::value == 42 work?

    Because there's actually an exception in the rule you think will cause this to fail.

    The rule, from [expr.prim.id.general]/3 is, emphasis mine:

    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 in which the object expression refers to the member's class51 or a class derived from that class, or
    • to form a pointer to member ([expr.unary.op]), or
    • if that id-expression denotes a non-static data member and it appears in an unevaluated operand.

    [Example 3:

    struct S {
      int m;
    };
    int i = sizeof(S::m);           // OK
    int j = sizeof(S::m + 42);      // OK
    

    end example]

    That third bullet right there: T::value is usable as an unevaluated operand. All the expressions you check in a requirement are unevaluated. So T::value works for non-static data members just fine.