Search code examples
c++c++20constexprc++-concepts

C++ Concept, how to check a constexpr static int is equal to 1, or similar...? Clang disagrees with GCC and MSVC


I can't find the correct syntax on cppreference for a concept that matches the value of a static constexpr member. This code compiles, and functions correctly on GCC and MSVC, but does not work in Clang. I was wondering if anybody knew if this was my mistake, or an issue with either GCC and MSVC or Clang? Here's a godbolt with all three compilers open, I think it illustrates the point!

#include <concepts>
#include <iostream>

template<typename T>
concept MyConcept = requires (T t){
    requires t.member == 1;

    //[clang's error]
    // note: because 't.member == 1' would be invalid: 
    //       constraint variable 't' cannot be used in an evaluated context
};

struct S {
    constexpr static int member {2};
};
struct D {
    constexpr static int member {1};
};

template<MyConcept T>
void func(){
    std::cout << "matched\n";
}

int main(){
    func<D>();
    return 0;
}

Solution

  • Clang is correct here (thanks, Casey).

    [expr.prim.req.nested]/2 says:

    A local parameter shall only appear as an unevaluated operand within the constraint-expression.

    And [expr.ref]/1 says:

    A postfix expression followed by a dot . or an arrow ->, optionally followed by the keyword template, and then followed by an id-expression, is a postfix expression. The postfix expression before the dot or arrow is evaluated;55 the result of that evaluation, together with the id-expression, determines the result of the entire postfix expression.

    In t.member, that evaluates t, and we're not allowed to do that in this context.

    A different way to express this would be to drop the requirement-body entirely and check the static member directly:

    template<typename T>
    concept MyConcept = T::member == 1;
    

    Concept definitions are arbitrary boolean expressions, so this is sufficient and doesn't run afoul of any rules.