简体   繁体   中英

Is there a reason we cannot name a non-static member function in an unevaluated context?

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?

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) ?

  • Should it be ignored? That can be done. But losing type information is rarely a good thing.
  • Should we retain it, and the type decltype evaluates to be a pointer to a member function, instead?
    That too would work, but now it's different from how data members would behave when named in an unevaluated context. We introduce a discrepancy.
  • Should it be retained, and the type resolved to something else? Perhaps something like 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.

Is there some ambiguity I'm missing?

I don't think so. It's probably more that there was a definite need to evaluate non-static data members and not so much for non-static member functions.

However, this:

static_assert(std::is_same<decltype(bar::foo), func>::value, "No Symmetry!");

doesn't make much sense. The type of &bar::foo is not func* , it's func bar::* . And there isn't a way to spell that without a pointer, so having to be able to evaluate decltype(bar::foo) means introducing whole new type syntax? Doesn't seem worth it.

Note that decltype(bar::foo) cannot be func because func is a function type, but bar::foo is a member function.

I don't see any reason.

Look at this:

typedef void Func();

Func freeFunction;

struct Foo {
    Func memberFunction;
};

freeFunction 's declaration is Func freeFunction; . So, decltype(freeFunction) is void() , ie Func .

Using the same logic, As Foo::memberFunction is declared as Func too, I'd expect decltype(Foo::memberFunction) to be Func too. But that's not the case, as this doesn't compile.

Just like a int a; decltype(a) int a; decltype(a) resolves to int , even if int a; is a member, decltype(Foo::memberFunction) should be OK.

Note, that qualifiers can be handled too, there is nothing special about them (of course, in this case, Func can only be used for declaring non-static member functions):

typedef void Func() const volatile &&;

struct Foo {
    Func memberFunction;
};

Here, I'd expect that decltype(Foo::memberFunction) to be void() const volatile && , so I can copy its declaration:

struct Bar {
    decltype(Foo::memberFunction) myMemFn;
};

Quote from the C++14 standard (9.2/11):

[ Note: The type of a non-static member function is an ordinary function type, and the type of a non-static data member is an ordinary object type. There are no special member function types or data member types. — end note ]

This quote means that it would be sensible, if decltype(<member_function>) returned a type of ordinary function.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM