When reading [expr.prim.id], one will see that
An id-expression that denotes a non-static data member or non-static member function of a class can only be used:
- if that id-expression denotes a non-static data member and it appears in an unevaluated operand.
The fact that the bullet above applies only to data members is unclear to me. Intuitively I'd expect the following to be well formed:
#include <type_traits>
using func = int();
class bar {
func foo; // This is valid, and not the subject of the question
};
static_assert(std::is_same<decltype(bar::foo), func>::value, "No Symmetry!");
But the decltype()
is ill-formed even before the static assertion is checked.
Is there some ambiguity I'm missing?
We can access the static member function using the class name or class' objects. If the static member function accesses any non-static data member or non-static member function, it throws an error. Here, the class_name is the name of the class.
A non-static member function is a function that is declared in a member specification of a class without a static or friend specifier. ( see static member functions and friend declaration for the effect of those keywords)
You cannot have static and nonstatic member functions with the same names and the same number and type of arguments. Like static data members, you may access a static member function f() of a class A without using an object of class A .
A non-static member function can be called only after instantiating the class as an object. This is not the case with static member functions. A static member function can be called, even when a class is not instantiated. A static member function cannot have access to the this pointer of the class.
Is there some ambiguity I'm missing?
The fact there's a whole lot of type information that is added as part of that member function declaration.
While func
may certainly be used to declare that member, the story doesn't end here. Once the member is declared, it's type is completed. That involves adding a couple of other things, like cv-qualifers and ref-qualifiers. In the case of foo
, all the default implicit ones are determined, and they become part of bar::foo
's type. As specified by [dcl.fct]/8:
The return type, the parameter-type-list, the ref-qualifier, the cv-qualifier-seq, and the exception specification, but not the default arguments, are part of the function type.
There's no way to specify them explicitly in the above declaration of foo
(though they may be added to func
), but they may be added in general:
class bar {
int foo() const volatile &&;
};
They are part of the function type, and decltype(bar::foo)
should address them if they appear (and if I gather correctly, even if they don't).
Where does the const volatile &&
go when we attempt to evaluate decltype(bar::foo)
?
decltype
evaluates to be a pointer to a member function, instead?int(foo const volatile&&)
or int() const volatile &&
(another form of function type)? That breaks the symmetry one would expect to have, and is again a discrepancy to data members. There is no easy or obvious way in which allowing it would always work well. So rather than complicate matters for a feature that would see limited use, it's better to regard it as ill-formed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With