1

This code compiles. Its obviously wrong because B can never be a WTF. Is there a way i can write the typecast so this is a compile time error?

class B{ public: virtual void abc(){} };
class D1 : public B{};
class WTF{ };

template<class T, class TT>
T DoSomething(TT o){
        return dynamic_cast<T>(o);
}
B*Factory() { return new D1; }
int main(){
        DoSomething<D1*, B*>(Factory());
        DoSomething<WTF*, B*>(Factory());
}
  • 2
    Related: http://stackoverflow.com/questions/874298/c-templates-that-accept-only-certain-types – Mat Oct 16 '11 at 09:13
  • what is wrong? `dynamic cast(B* pointer)` will return 0 – Ruggero Turra Oct 16 '11 at 09:17
  • @wiso: Sometimes its valid for my factory to produce 0. I can do an run time assert but i like a compile time solution. –  Oct 16 '11 at 09:21

4 Answers4

3

No, you can't rewrite the cast to be a compiler time error, principally because your assertion that a B can never be a WTF is false.

E.g.

class Combine : public B, public WTF
{
};

int main()
{
        Combine c;
        std::cout << (void*)&c << '\n';
        std::cout << (void*)dynamic_cast<WTF*>(&c) << '\n';
        return 0;
}
CB Bailey
  • 755,051
  • 104
  • 632
  • 656
  • !@#$%^&*(. I guess i need to write a cast that will assert or throw if the input is non 0 and the output is 0 –  Oct 16 '11 at 09:40
  • No, you are wrong. You **can** rewrite the cast to be a compile-time error. Just use static_cast and never create a sub-class of WTF that inherits from B. –  Oct 16 '11 at 09:59
  • @Atom: `static_cast` does something completely different. Yes, it gives a compiler time error, but it gives a compile time error for some things were a `dynamic_cast` would actually succeed. It also throws away the run time checks of `dynamic_cast`, "succeeding" without errors in cases where the cast should fail. – CB Bailey Oct 16 '11 at 10:03
  • @CharlesBailey: The assertion that a B can never be a WTF is true, if you design your class hierarchy so that the assertion is true. –  Oct 16 '11 at 18:14
  • @Atom: Only if your class hierarchy is closed. There is no technical restriction on a client of the library binding together the `B` and `WTF` hierarchies. That is why the `dynamic_cast` can't be replaced by something with the equivalent functionality except giving a compile-time error. It's just not possible. – CB Bailey Oct 16 '11 at 18:17
  • @CharlesBailey: Yes. That's why I wrote in my answer that it works only if the programmer(s) know what they are doing. –  Oct 16 '11 at 19:07
  • @Atom: I'm not sure what point you are driving at. Your first solution is to replace `dynamic_cast` with `static_cast` which isn't an equivalent change and the second solution, if I understand it, is to make no change which doesn't result in a compile time error. You seem to be backing up my assertion that it is not possible to replace the original cast with something equivalent other that moving the error from run time to compile time. – CB Bailey Oct 16 '11 at 19:21
  • @CharlesBailey: In my opinion, there seems to be nothing in the question's C++ code that would allow us to state that static_cast and dynamic_cast are different in this particular context. The question creates D1, converts it to B, which is converted back into D1 (D1 -> B -> D1). This is the use case mentioned in the question. X -> B -> X. There is no mention of anything like X -> B -> Y, which would yield NULL if dynamic cast is used. So, in relation to the question, static cast and dynamic cast seem to be equivalent there. But maybe I am wrong about this. –  Oct 16 '11 at 19:52
  • @CharlesBailey: Sorry, I forgot to write that I was talking about the case then Y inherits from B. Y is unlike WTF. –  Oct 16 '11 at 20:00
2

If your compiler/library support is behind the times and you don't have traits available, this will perform the simple check:

template<class T, class TT>
T DoSomething(TT o) {
  enum { Concept_SimpleTypeRelationCheck = static_cast<T>(0) == static_cast<TT>(0) };
  return dynamic_cast<T>(o);
}

With multiple inheritance, dynamic_cast<T>(o) could still fail (or succeed despite compilation error, as others have noted).

Note that this variant differs from the accepted answer (downcast via static_cast) in that its use of dynamic_cast preserves type safety when downcasting because it can return 0 if the type does not match the destination. static_cast is fine when upcasting through single inheritance hierarchies, but unsafe for downcasting (unless you have manually ensured correctness in each case).

justin
  • 104,054
  • 14
  • 179
  • 226
2

There are two solutions to this problem, depending on what you want the code to be doing.

The first solution: Change the dynamic_cast into static_cast. This solution assumes that you never use class WTF in a class hierarchy with multiple inheritance. See below.

The second solution: You realize that C++ allows multiple inheritance. There is no way for you nor for the compiler to predict whether an arbitrary instance of WTF created sometime in the future will inherit from class B or not.

#include <stdio.h>

class B{ public: virtual void abc(){} };
class D1 : public B{};
class WTF{ };
class WTF_B : public WTF, public B {};

template<class T, class TT>
T DoSomething(TT o){
    return dynamic_cast<T>(o);
}

B*Factory1() { return new D1; }
B*Factory2() { return new WTF_B; }

int main(){
    printf("%p\n", DoSomething<D1*, B*>(Factory1()));
    printf("%p\n", DoSomething<WTF*, B*>(Factory1()));
    printf("%p\n", DoSomething<WTF*, B*>(Factory2()));
    return 0;
}

The above source code can also be found here. Its output looks like this:

0x861a008
(nil)
0x861a028

In either case, the choice between solution 1 and solution 2 is yours - but the choice is yours only if you know what you are doing.

  • Well written. I will choose static because the T is ALWAYS the class being created however due because the factory function needs to lookup the id then create it via huge (generated) id list it has to return a base. The template problem actually happened because i changed the signature in one of the overload causing a mysterious assert saying my IO is wrong. I forgot and didnt realize there was another template i must update. I didnt realize static cast can be used for an upcast –  Oct 16 '11 at 10:02
  • 1
    @acidzombie24 `static_cast` here is a brute force cast (Solution 1). Your example in the OP depicts a cast to derived from base. `static_cast` will work for upcasting, but fail for downcasting. In this case, `static_cast` will silently fail on you: where `dynamic_cast` would return `0`, `static_cast` will return the argument. If you are using single inheritance and need correct dynamic downcasting, reread my answer. – justin Oct 16 '11 at 10:34
  • @Justin ah ha. Well, i cant accept multiple answers but i'll +1 your comment and hopefully ppl read your answer as well (also i +1 your answer when i first read it) –  Oct 17 '11 at 00:21
  • @acidzombie24 That's no problem - I was more interested in correct semantics than rep. I've also expanded the answer with some of the info in this dialog. cheers. – justin Oct 17 '11 at 02:24
  • Atom you should mention how unsafe static_cast ing is. CC @Justin http://stackoverflow.com/questions/7789326/when-is-static-cast-safe-for-upcasting –  Oct 17 '11 at 04:18
1

There is no way to make the compiler give an error about an impossible cast in this situation because it is not an impossible cast. There is the possibility that somewhere there is a class that has multiple inheritance to derive from both WTF and B.

IronMensan
  • 6,761
  • 1
  • 26
  • 35