转换模板持有多态类型时未定义的行为

m.s*_*.s. 5 c++ polymorphism gcc type-erasure undefined-behavior

由于类型擦除的原因,我有一个A<T>可以保存任何数据类型的模板。当持有派生自 的A多态类型并将其转换为 时,GCC 的未定义行为清理程序会报告运行时错误:DerivedBaseA<Base>

#include <iostream>

struct I
{
    virtual ~I() = default;
};

template<typename T> 
struct A : public I
{
    explicit A(T&& value) : value(std::move(value)) {}
    T& get() { return value; }
private:
    T value;
};

struct Base
{
    virtual ~Base() = default;
    virtual void fun() 
    {
        std::cout << "Derived" << std::endl;
    }
};

struct Derived : Base
{
    void fun() override
    {
        std::cout << "Derived" << std::endl;
    }
};

int main()
{
    I* a_holding_derived = new A<Derived>(Derived());
    A<Base>* a_base = static_cast<A<Base>*>(a_holding_derived);
    Base& b = a_base->get();
    b.fun();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

编译并运行

$ g++ -fsanitize=undefined -g -std=c++11 -O0 -fno-omit-frame-pointer && ./a.out
Run Code Online (Sandbox Code Playgroud)

输出:

main.cpp:37:62: runtime error: downcast of address 0x000001902c20 which does not point to an object of type 'A'

0x000001902c20: note: object is of type 'A<Derived>'

 00 00 00 00  20 1e 40 00 00 00 00 00  40 1e 40 00 00 00 00 00  00 00 00 00 00 00 00 00  21 00 00 00

              ^~~~~~~~~~~~~~~~~~~~~~~

              vptr for 'A<Derived>'

    #0 0x400e96 in main /tmp/1450529422.93451/main.cpp:37

    #1 0x7f35cb1a176c in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2176c)

    #2 0x400be8  (/tmp/1450529422.93451/a.out+0x400be8)


main.cpp:38:27: runtime error: member call on address 0x000001902c20 which does not point to an object of type 'A'

0x000001902c20: note: object is of type 'A<Derived>'

 00 00 00 00  20 1e 40 00 00 00 00 00  40 1e 40 00 00 00 00 00  00 00 00 00 00 00 00 00  21 00 00 00

              ^~~~~~~~~~~~~~~~~~~~~~~

              vptr for 'A<Derived>'

    #0 0x400f5b in main /tmp/1450529422.93451/main.cpp:38

    #1 0x7f35cb1a176c in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2176c)

    #2 0x400be8  (/tmp/1450529422.93451/a.out+0x400be8)


Derived
Run Code Online (Sandbox Code Playgroud)

live example on coliru

我有两个问题:

  1. 消毒器的输出是否正确?
  2. A<Derived>如果是,从到 的有效转换会是什么样子A<Base>

Die*_*ühl 5

问题在于A<Base>,彼此之间根本A<Derived>没有任何关系。他们的表现可能完全不同。对于您尝试执行的转换,有必要有一个基类,但显然情况并非如此。A<Base>A<Derived>

看来,您想要创建类似智能指针的东西,其行为类似于值类型。我不确定是否可以创建一个支持所有必要转换的值类型。如果需要支持转换的类型组中有特定需求或已知的公共基类,则可以实现相应的类。