4

Both Clang and GCC refuse to compile this but MSVC doesn't.

class Foo {
    int name;
};

template <class T>
concept HasName = requires { T::name; };

static_assert(HasName<Foo>);

MSVC only checks if the name exists, but it doesn't enforce access restrictions (i.e., private), and allows member access from without a class instance/object.


Addition from comments below:
Making name public allows to code to compile. But name is a non-static member, so why it is allowed to refer to it as T::name inside the requires ?

5
  • Note that if you change name to be public, they all accept it: see demo. Commented Aug 16 at 9:15
  • @wohlstad OK, how is that well formed? T::name when "name" isn't a static class member. Commented Aug 16 at 9:24
  • First of all, all compilers accept this if you make name public but keep it non-static. You're allowed to access non-static members without an object in (most?) unevaluated contexts. Commented Aug 16 at 9:46
  • As for the missing access check, this looks like an obvious MSVC bug. Commented Aug 16 at 9:46
  • 1
    Reading cppreference, item 3 under Usage, I see that you can refer to a non static data member like that in an unevaluated context. cppreference has an example with sizeof(S::m) where S is a class and m a non-static member. Commented Aug 16 at 10:13

2 Answers 2

3

As already mentioned, MSVC is wrong to accept the code since name member is private.

However - you also seems to be interested (based on the comments) to know why making it public causes the code to be valid (and accepted by the major compilers - see demo).
Specifically since name is a non-static member why is it OK to use it as T::name in the requires expression.

The reason is that in an unevaluated context you can use this syntax to refer to non-static members.

This is mentioned in item 3 of the Usage section in this cppreference page:

  1. (for data members only, not member functions) When used in unevaluated operands.
    struct S
    {
       int m;
       static const std::size_t sz = sizeof m; // OK: m in unevaluated operand
    };
    
    std::size_t j = sizeof(S::m + 42); // OK: even though there is no "this" object for m
    

Notes: such uses are allowed via the resolution of CWG issue 613 in N2253, which is treated as a change in C++11 by some compilers (e.g. clang).

(emphasis is mine)

As you can see S::m is valid inside sizeof (even though m is non-static) because it is an unevaluated context, just like in your case.

Sign up to request clarification or add additional context in comments.

Comments

2

MSVC doesn't take into account name's access, which is an obvious bug in the compiler. Work it around by prepending an &:

class Foo {
    int name;
};

template <class T>
concept HasName = requires { &T::name; };

static_assert(HasName<Foo>);

This is rejected by all three compilers. And, all three accept it, if name's access is public.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.