Considering the code example shown below, is decltype(h{}.t());
a legal expression? Which specific rule in the C++20 standard either allows or forbids this?
struct d;
struct h { static auto t() -> d; };
using a = decltype(h::t()); // all ok
using b = decltype(decltype(h{})::t()); // all ok
using c = decltype(h{}.t()); // clang ok, gcc ok, msvc nope
The error message produced by MSVC:
<source>(6): error C2027: use of undefined type 'd'
<source>(1): note: see declaration of 'd'
Having stumbled upon yet another bug with MSVC, I've actually discovered a workaround for MSVC's deviating behavior. By simply providing a user-defined defaulted default constructor for type h
, MSVC no longer complains about d
being undefined.
struct d;
struct h {
static auto t() -> d;
h() = default;
};
using c = decltype(h{}.t()); // all ok
However, the problem still seems to persist when skipping use of h
's constructor, like with the use of std::declval
for example (as in: decltype(std::declval<h>().t());
). Also, be aware that by providing a user-defined default constructor, even when defaulted, this will cause h
to no longer be considered an aggregate type.
It is explicitly permitted for the type of a prvalue expression operand to decltype
to be incomplete. Temporary materialization is exceptionally not applied in this circumstance, so that a destructor of d
isn't necessary either (which would require d
to be complete to lookup the destructor). So d
doesn't need to be complete. For reference to the standard see [dcl.type.decltype]/2.
The function t
may also be undefined, because use in a decltype
operand is not use in a potentially-evaluated expression and therefore not an odr-use.
Constructing a h
object with h{}
also doesn't demand either t
to be defined or d
to be complete.
The standard even includes a very similar example (see link above).
So MSVC is wrong here.