8

I want to know what the modern C++11 equivalent of Java's instanceof. I have seen this SO post but it is quite old and was wondering if there's a more modern, better solution in C++11?

I was hoping there's a possibility of using a switch construct without having to resort to a manual enum class.

class A {

};

class B : public A {

}

class C : public A {

}

on_event(A& obj)
{
    switch (obj) {
       case A:
       case B:
       case C:
    }
}

My base class does not have any virtual methods or functions. I am representing an expression tree for a parser and the base class is just a polymorphic holder - like an ADT in Haskell/OCaml.

Community
  • 1
  • 1
pezpezpez
  • 654
  • 8
  • 15
  • 3
    Are you asking for `dynamic_cast<>`? That's not c++11 specific. – πάντα ῥεῖ Oct 12 '14 at 12:54
  • Nothing changed: there's no reflection in standard C++. If you want to switch on runtime type information, you `dynamic_cast` – quantdev Oct 12 '14 at 12:54
  • Maybe of interest: http://stackoverflow.com/q/25495733/596781 – Kerrek SB Oct 12 '14 at 13:05
  • 1
    -1 The question is now absurd: You're asking for an "equivalent to Java" of something that is completely unrelated to and uncomparable with Java. – Kerrek SB Oct 12 '14 at 13:06
  • Also, it seems insane to want to manually reinvent something which is precisely solved by runtime polymorphism in the language. – Kerrek SB Oct 12 '14 at 13:07
  • 2
    I don't see how it is "absurd" and you come across as rude to me. I want to know the best way to implement my on_event function to handle the different sub-classes that doesn't have virtual methods/members in the base class (A). I know how I would do it in Java but wanting to know the best way in C++. – pezpezpez Oct 12 '14 at 13:11
  • 6
    _"My base class does not have any virtual methods or functions"_ and _"the base class is just a polymorphic holder"_ appear to be contradicting statements. Since `A` is a _base class_ it **should have a virtual destructor**. This sounds like an XY problem anyway, chances are you can avoid the `switch` statement altogether by using virtual functions. – Captain Obvlious Oct 12 '14 at 13:15
  • Despite it having been downvoted, I want to draw your attention to @Kastaneda 's answer, which is definitely useful - even if not for the case of accessing subclasses through a pointer or a reference. – einpoklum Mar 28 '17 at 20:16

5 Answers5

12

The same answer still applies, and has always been like this in C++:

if (C * p = dynamic_cast<C *>(&obj))
{
    // The type of obj is or is derived from C
}
else
{
    // obj is not a C
}

This construction requires A to be polymorphic, i.e. to have virtual member functions.

Also note that this behaviour is different from comparing typeid(obj) == typeid(C), since the latter tests for exact type identity, whereas the dynamic cast, as well as Java's instanceof, only test for the target type to be a base class of the type of the most-derived object.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • 1
    it also requires `RTTI`, which is kinda of a pain for some C++ users . – user2485710 Oct 12 '14 at 12:57
  • 1
    @user2485710: I don't think that statement makes sense in the context of the *language* C++. The code is valid C++, without qualification. – Kerrek SB Oct 12 '14 at 12:57
  • 1
    if you don't have `RTTI` that thing will not work, doesn't matter what you are considering, it's just required in order to let that piece of code work properly . – user2485710 Oct 12 '14 at 12:58
  • @user2485710: On some IDEs (for example, Visual Studio), you need to enable the RTTI option in the project settings, but that indeed has nothing to do with the C++ language standard. – barak manos Oct 12 '14 at 12:59
  • 4
    @user2485710: There's no such thing in C++. You cannot "not have RTTI" and still have C++. – Kerrek SB Oct 12 '14 at 12:59
  • What if A doesn't have any members of functions; will this still work then? – pezpezpez Oct 12 '14 at 12:59
  • 1
    @user8090: No, it needs to have at least one virtual member function. (Typically a virtual destructor would furnish a canonical virtual member function.) [[One could possibly make an argument that if you have a program that legitimately doesn't require virtual destructors, then it doesn't require run-time polymorphism at all, possibly after some heavy refactoring.]] – Kerrek SB Oct 12 '14 at 12:59
  • @user8090: Must have at least one virtual member function (otherwise, the class will not have a V-Table, and the `dynamic_cast` operator will not be able to make the type-comparison). – barak manos Oct 12 '14 at 13:00
  • 1
    @KerrekSB `You cannot "not have RTTI" and still have C++` that's a good approach to life in general, too good to be true . – user2485710 Oct 12 '14 at 13:02
  • I'll edit my question as my base class doesn't have any virtual members or functions. – pezpezpez Oct 12 '14 at 13:02
  • 4
    @user8090: Don't do that. It would make the question absurd. You're asking for a Java equivalent, but *everything* in Java is polymorphic. Ask a new question if you need to solve something C++ specific. – Kerrek SB Oct 12 '14 at 13:03
  • 2
    Since this question is tagged C++11, you could replace `C *` with `auto` ;) – fredoverflow Oct 12 '14 at 13:20
  • 1
    @FredOverflow: It's poor hygiene to hide pointers in type aliases, so at best you should say `auto *`. I'm aware of that, but I figured `C *` is a bit shorter and cleaner in this case. – Kerrek SB Oct 12 '14 at 17:26
  • @KerrekSB Heh you should tell Microsoft that. Literally every pointer in the win32 api is hidden in a type alias. – Navin Oct 18 '15 at 18:14
  • Shouldnt the if statement be if (C * p == dynamic_cast(&obj)) with two ='s? – Legion Daeth Nov 24 '16 at 09:50
  • @LegionDaeth: No, that wouldn't be valid syntax, nor would it be meaningful. What is `p`? – Kerrek SB Nov 24 '16 at 09:53
  • @KerrekSB Nevermind, misunderstood what was going on, you're right. – Legion Daeth Nov 24 '16 at 10:02
0

In C++ plain old data (POD) has no runtime type information. The classes described all take exactly 1 byte, and have identical runtime representations in any compiler with the empty base class optimization.

As such what you want cannot be done.

Adding a virtual destructor to the base class adds in RTTI, and dynamic_cast support.

Adding an enum or int field to the base that gets initialized differently for each derived class also works.

Yet another option is to create a template function, and store a pointer to it, like so:

using my_type_id=void(*)();
template<class>void get_my_type_id_helper(){};
template<class T> my_type_id get_my_type_id(){return get_my_type_id_helper<T>;}

and then storing a my_type_id in A initialized appropriately. This is reinventing RTTI, and as you want more features you will approach C++ RTTI overhead.

In C++ you only pay for what you ask for: you can ask for classes without RTTI, which you did, and get it.

RTTI is Run Time Type Information. POD is plain old data, a C++03 term. Many classes are not POD: the easy way is to add a virtual destructor. C++11 has more fine grained standard layout and aggregate terms.

Technically RTTI and POD are not opposites of each other: there are classes with no RTTI that are not POD.

Note that MSVC has options to not generate RTTI and its aggressive Comdat folding can break the manual RTTI I did above, in both cases in violation of the standard.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
0

Maybe you are interested in the answer I've posted inside your mentioned old SO post.

https://stackoverflow.com/a/49296405/1266588

The answer presents an implementation of instanceof without the usage of dynamic_cast based on C++11, template metaprogramming and RTTI. A small performance measurement application demonstrates that it is more efficient than dynamic_cast if you use compiler optimization.

andi1337
  • 191
  • 1
  • 3
-1

If you're willing to limit yourself to types known at compile-time (rather than working through pointers on instances of classes with vtables) - then C++11 and later does have an instanceof equivalent: It is std::is_base_of.

You might also want to check out std::is_convertible and std::is_same.

einpoklum
  • 118,144
  • 57
  • 340
  • 684
Kastaneda
  • 739
  • 1
  • 8
  • 15
  • 3
    I'm not sure how that helps him unless he makes it a function template and knows the types at compile-time. I think it's pretty clear that he's talking about runtime polymorphism – PeterT Oct 12 '14 at 13:33
  • @PeterT: Completely disagree. That is, in C++, especially Modern C++, we do a whole lot with compile-time-known types, so this is a perfectly valid answer (even if it should be edited). – einpoklum Mar 28 '17 at 20:12
-1

Don't do that. In most cases you should review your design when you ask for instanceof or dynamic_cast.

Why? You are most likely violating Liskov's substitiontin principle.

How about this approach:

class A {
   public:
     virtual void action();
     virtual ~A();
};

class B : public A {
   public: void action() override;
};

class C : public A {
   public: void action() override;
};

void on_event(A& obj)
{
   obj.action();
}

Note that as @Yakk pointed out you need at least one virtual method anyway to get dynamic polymorphism. And there is a rule that says: When you have at least one virtual method, always also write a virtual destructor in the base class.

You can do all this with templates and specialization or type tagging but I take from your question -- coming from Java -- you don't want to go there yet. You really like virtual methods, don't you? Sorry, that you have to mark them in C++.

towi
  • 21,587
  • 28
  • 106
  • 187
  • I don't think that a program that consists of 3 classes (A, B, C) violates LSP and may be considered as a good design. I wanted to downvote cause LSP part is misleading (the question is about direct alternative to instanceof for C++11 and your warnings was already mentioned in the link), but someone may finds it useful. – m039 Apr 26 '17 at 14:29