【发布时间】:2015-12-19 13:00:02
【问题描述】:
出于类型擦除的原因,我有一个模板A<T>,它可以保存任何数据类型。当A 持有派生自Base 的多态类型Derived 并将其转换为A<Base> 时,GCC 的未定义行为清理程序会报告运行时错误:
#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;
}
编译并运行
$ g++ -fsanitize=undefined -g -std=c++11 -O0 -fno-omit-frame-pointer && ./a.out
输出:
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
我有两个问题:
- 消毒剂的输出是否正确?
- 如果是,从
A<Derived>到A<Base>的有效转换是什么样的?
【问题讨论】:
-
从
I*到X的静态转换具有未定义的行为,因为它实际上指向Y的子对象,而X和Y是不同的类型(分别为@987654338 @ 和A<Derived>)。 -
@KerrekSB 我确实有另一种环绕
A的类型;我没有在这里展示它以保持示例最小化。 -
OK - 在这种情况下,您必须使用
I专门作为您的“类型擦除句柄”。公共 API 的每个语义方面都必须通过I实现。 -
@KerrekSB 我可以使用两个嵌套的
static_casts,但是我需要知道原始类型(以某种方式破坏了类型擦除的目的):example code;有什么办法吗? -
我不确定问题是否明确。类型擦除不会给你神奇的力量。它解决了需要在接口中编码的非常特定的问题。例如,
any是类型擦除类的最简单示例,其唯一接口是“类型检查”。std::function是一个不同的类型擦除类,其接口是“函数调用运算符”。您需要记住一个要以类型擦除的方式提供的接口。由于您的I是空的,因此您目前没有完成任何事情。
标签: c++ gcc polymorphism undefined-behavior type-erasure