Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

typeid doesn't work with non-static member function

clang doesn't compile the third call to typeid below (see live example). But I can't see anything in §5.2.8 that disallows this, specially when we consider that the expression B::f is not a glvalue of polymorphic class type (see paragraph 3). Also, according to this paragraph the expression B::f is an unevaluated operand, and as such, the call typeid(B::f) should compile. Note that GCC doesn't compile any of the calls to typeid below:

#include <iostream>
#include <typeinfo>

struct A{ int i; };
struct B{ int i; void f(); };

int main()
{
    std::cout << typeid(A::i).name() << '\n';
    std::cout << typeid(B::i).name() << '\n';
    std::cout << typeid(B::f).name() << '\n';
}
like image 812
Belloc Avatar asked Jan 20 '15 19:01

Belloc


People also ask

What is the use of typeid() function?

The typeid operator allows the type of an object to be determined at run time. The result of typeid is a const type_info& . The value is a reference to a type_info object that represents either the type-id or the type of the expression, depending on which form of typeid is used.

What is non static member function?

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)

What is typeid in c++?

The typeid operator provides a program with the ability to retrieve the actual derived type of the object referred to by a pointer or a reference. This operator, along with the dynamic_cast operator, are provided for runtime type identification (RTTI) support in C++.

What happens if non static members are used in static member function?

What happens if non static members are used in static member function? Explanation: There must be specific memory space allocated for the data members before the static member functions uses them. But the space is not reserved if object is not declared.


2 Answers

As far as I can tell clang is correct, using a non static member is only valid in an unevaluated context if it is a data member. So it looks like gcc is incorrect for the first two cases, but gcc works correctly in the case of sizeof and decltype which also have unevaluated operands.

From the draft C++11 standard section 5.1.1 [expr.prim.general]:

An id-expression that denotes a non-static data member or non-static member function of a class can only be used:

and includes the following bullet:

if that id-expression denotes a non-static data member and it appears in an unevaluated operand. [ Example:

struct S {
    int m;
};
int i = sizeof(S::m); // OK
int j = sizeof(S::m + 42); // OK 

—end example ]

The rest of the bullets do not apply, they are as follows:

  • as part of a class member access (5.2.5) in which the object expression refers to the member’s class61 or a class derived from that class, or
  • to form a pointer to member (5.3.1), or
  • in a mem-initializer for a constructor for that class or for a class derived from that class (12.6.2), or
  • in a brace-or-equal-initializer for a non-static data member of that class or of a class derived from that class (12.6.2), or

We know that operand is unevaluated from section 5.2.8 which says:

When typeid is applied to an expression other than a glvalue of a polymorphic class type, [...] The expression is an unevaluated operand (Clause 5).

We can see from the grammar that an id-expression is either an unqualified-id or a qualified-id:

id-expression:
    unqualified-id
    qualified-id

Update

Filed a gcc bug report: typeid does not allow an id-expression that denotes a non-static data member.

like image 167
Shafik Yaghmour Avatar answered Oct 15 '22 17:10

Shafik Yaghmour


typeid(A::i).name() doesn't quite do what I thought it would do. I expected it to be a pointer-to-member, but it's actually just an int.

To see this, run this code:

#include <iostream>
struct A{ int i; };
struct B{ int i; void f(void); };

template<typename T>
void what_is_my_type() {
    std:: cout << __PRETTY_FUNCTION__ << std:: endl;
}

int main()
{
    what_is_my_type<decltype(&A::i)>(); // "void what_is_my_type() [T = int A::*]"
    what_is_my_type<decltype(&B::i)>(); // "void what_is_my_type() [T = int B::*]"
    what_is_my_type<decltype(&B::f)>(); // "void what_is_my_type() [T = void (B::*)()]"

    what_is_my_type<decltype(A::i)>();  // "void what_is_my_type() [T = int]"
    what_is_my_type<decltype(B::i)>();  // "void what_is_my_type() [T = int]"
    // what_is_my_type<decltype(B::f)>();       //    doesn't compile

}

I've put the output in a comment after each call.

The first three calls work as expected - all three work and the type information includes the type of the struct (A or B) as well as the type of member.

The last three are different though. The final one doesn't even compile, and the first two simply print int. I think this is a clue as to what is wrong. It is possible, given a particular A or B, to take the address of that particular member:

A a;
int * x = &(a.i);
*x = 32;

but it is not possible (or even meaningful?) to do this:

B b;
???   y = &(a.f); // what does this even mean?

Finally, to emphasize that this is not about pointers, consider this:

A a;
B b;
int x = a.i;
int y = b.i;
??? z = b.f;  // what would this mean? What's its type?
like image 29
Aaron McDaid Avatar answered Oct 15 '22 18:10

Aaron McDaid