-1

I am pretty sure this is a compiler bug or something: if two types in different translation units have the same name and derive from nested classes of a template class, dynamic_cast will fail in one of those translation units.

In my case, I use two different types named A in two translation units (they were test cases). In each, I have an object obj of type A. A is derived from an abstract base root_type. obj_ref has type root_type& and is bound to obj. Attempting to cast obj_ref to A& throws std::bad_cast.

mixin.hpp
#pragma once
template <typename... Types>
struct mixin
{
    struct root_type
    {
        virtual ~root_type() = default;
    };
};
use_AB.cpp
#include "mixin.hpp"
struct A;
struct B;
struct A : mixin<A, B>::root_type{};

void use_AB()
{
    using root_type = mixin<A, B>::root_type;
    A a;
    root_type &a_ref = a;
    dynamic_cast<A&>(a_ref);
}
use_A.cpp
#include "mixin.hpp"
struct A;
struct A : mixin<A>::root_type {};

void use_A()
{
    using root_type = mixin<A>::root_type;
    A a;
    root_type &a_ref = a;
    //////////////////////////////////////////
    dynamic_cast<A&>(a_ref); // throws - dynamic_cast failure
    //////////////////////////////////////////
}
main.cpp
void use_A();
void use_AB();

int main()
{
    use_A();
    use_AB();
    return 0;
}

What’s going on?

Compiler is VisualStudio 2015 (v140).

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
Kietz
  • 1,186
  • 11
  • 19
  • 2
    Please provide a [mcve]. – Barry Mar 19 '16 at 18:40
  • @Barry still minimizing the repro – Kietz Mar 19 '16 at 18:54
  • No you should provide code for type `A` and `root_type` because if `root_type` is not polymorphic then `dynamic_cast` can't be used. – Jean-Baptiste Yunès Mar 19 '16 at 18:54
  • @Jean-BaptisteYunès root_type is abstract which implies that both are polymorphic – Kietz Mar 19 '16 at 18:56
  • 1
    *"I am intentionally leaving out details..."* why would you do this? – Jonathan Potter Mar 19 '16 at 19:14
  • @Barry @JohnathanPotter The error goes away when I copy the code into a different namespace. Since I can't even reliably reproduce the error by copying code on my own computer, I cannot give an example which I would call verifiable. That aside, the implementation of `root_type` and `A` are not relevant to my question, which is whether (given the assertions which I make) the cast **could** throw. Can it? – Kietz Mar 19 '16 at 19:20
  • @Kietz Clearly they're relevant to the question. If you can't reproduce the error, then you have an incorrect perception of what the problem is. – Barry Mar 19 '16 at 19:23
  • He he, `volatile`. Andrei Alexandrescu used to do that, or at least discussed it, as I recall, with `volatile` as a kind of type checked **tag**. – Cheers and hth. - Alf Mar 19 '16 at 19:50
  • **unable to reproduce** with Visual C++ 2015 update 2, with or without exception or RTTI enabled. – Cheers and hth. - Alf Mar 19 '16 at 19:53
  • @Cheersandhth.-Alf could you try again with at least two different `cpp` files? – Kietz Mar 21 '16 at 22:18
  • @Kietz: No need. After you posted the more Real Code™ you have an answer already, an ODR violation. – Cheers and hth. - Alf Mar 21 '16 at 23:19

1 Answers1

0

You violate the One Definition Rule by giving struct A two different definitions:

struct A : mixin<A, B>::root_type{};
struct A : mixin<A>::root_type {};

Therefore your program has undefined behavior, and the compiler is not required to diagnose the problem.

aschepler
  • 70,891
  • 9
  • 107
  • 161
  • Yep, that was it. I'll put those classes in `namespace {}` in the future (that should fix it, right?). I'm still surprised the compiler didn't give me any error, though... – Kietz Mar 21 '16 at 22:25
  • @Kietz: How would it detect this error? Different translation units are translated completely independently. This is the reason the standard does not require detection of this violation, – AnT stands with Russia Mar 21 '16 at 23:20
  • Sorry, then I mean a linker error – Kietz Mar 21 '16 at 23:23
  • On reflection, there is no way a linker error could be generated unless the linker examined the two `A` classes for equivalence, which is a crazy thing to expect – Kietz Mar 24 '16 at 15:14