我需要一种方法来获取指向C ++中对象开头的指针。此对象在模板中使用,因此它可以是任何类型(多态或非多态),并且可能是使用多继承的对象。
我发现this article描述了一种方法(在“T”是多态类型的情况下,使用typeid和“dynamic_cast”来描述一种方法(参见“Dynamic Casts”一节)。
这在MSVC上运行得非常好,但是在GCC(4.x)上,当它与非多态类型一起使用时,它似乎落在它的屁股上并吐出编译器错误。
有没有人知道如何:
以下是我目前用来尝试实现此目的的代码。
template <typename T>
void* dynamicCastToVoidPtr(T *const ptr)
{
// This is done using a separate function to avoid a compiler error on some
// compilers about non-polymorphic types when calling startOfObject
return dynamic_cast<void*>(ptr);
}
template <typename T>
void* startOfObject(T *const ptr)
{
// In cases of multiple inheritance, a pointer may point to an offset within
// another object
// This code uses a dynamic_cast to a void* to ensure that the pointer value
// is the start of an object and not some offset within an object
void *start = static_cast<void*>(ptr);
if(start)
typeid(start = dynamicCastToVoidPtr(ptr), *ptr);
return start;
}
template <typename T>
void doSomethingWithInstance(T *const instance)
{
// Here is where I need to get a void* to the start of the object
// You can think of this as the deleteInstance function of my memory pool
// where the void* passed into freeMemory should point to the
// start of the memory that the memory pool returned previously
void *start = startOfObject(instance);
if(start)
allocator->freeMemory(start);
}
感谢。
答案 0 :(得分:1)
gcc确切的错误消息是
错误:无法使用dynamic_cast
&n
(类型为struct N*
)来键入void*
(源类型不是多态的)
这可以通过将boost::is_polymorphic
与boost::enable_if
和boost::disable_if
结合使用来处理,遗憾的是gcc会采用明显的方法进行处理,所以这是解决方法:
template <class T>
void* address_of_impl(T* p, boost::enable_if< boost::is_polymorphic<T>, int >)
{
return dynamic_cast<void*>(p);
}
template <class T>
void* address_of_impl(T* p, ...) { return static_cast<void*>(p); }
template <class T>
void* address_of(T* p) { return address_of_impl(p, 0); }
我们在哪里使用SFINAE(省略号总是在重载分辨率中被认为是最后一次,因此编译器首先尝试使用因dynamic_cast
)而导致非多态类型失败的enable_if
版本。 / p>
我在gcc 3.4上测试它并且它通过了。我正在调查another question为什么使用disable_if
代替...
无效。
修改强>:
这是一个简单的拼写错误(忘了::type
位):
template <class T>
typename boost::enable_if< boost::is_polymorphic<T>, void* >::type
address_of(T* p) { return dynamic_cast<void*>(p); }
template <class T>
typename boost::disable_if< boost::is_polymorphic<T>, void* >::type
address_of(T* p) { return static_cast<void*>(p); }
答案 1 :(得分:0)
我做了一些调查,你可能在GCC发现了一个错误;我会report it。但是,可能有一些我无法找到的规则,即在这种情况下需要实例化逗号运算符左侧的模板函数,即使未评估整个表达式;在这种情况下,文章中的技术是错误的。
我建议你看看boost::type_traits::is_polymorphic是否适合你。
答案 2 :(得分:0)
我找到了来自another question的解决方案,如果一个类型是多态的,我可以在编译时解决这个问题,然后我可以使用模板特化来使用正确的类型转换。显然,如果编译器在子对象之间添加填充,这个方法可能会中断,但我希望在一些已知的情况下添加一些编译时断言来捕获它。它可以在MSVC和GCC上正确编译和运行。
如果类型是多态的,这是要解决的代码。
#define SIMPLE_POLYMORPHIC(TYPE, POLYMORPHIC) \
template <> \
struct IsPolymorphic<TYPE> \
{ \
static const bool value = POLYMORPHIC; \
};
template <typename T>
struct IsPolymorphic
{
struct Derived : public T { virtual ~Derived(); };
static const bool value = (sizeof(Derived) == sizeof(T));
};
SIMPLE_POLYMORPHIC(int, false);
SIMPLE_POLYMORPHIC(unsigned int, false);
// ... do this for all intrinsic or non-derivable types
根据类型是否为多态来执行转换的代码。
template <typename T, bool isPolymorphic = IsPolymorphic<T>::value>
struct StartOfObject
{
static void* getStart(T *const ptr)
{
return static_cast<void*>(ptr);
}
};
template <typename T>
struct StartOfObject<T, true>
{
static void* getStart(T *const ptr)
{
if(ptr)
return dynamic_cast<void*>(ptr);
return NULL;
}
};
还有一个测试案例。
#define CLASS_STUFF(CLASS) \
public: \
CLASS() {} \
virtual ~CLASS() {} \
int m_##CLASS;
class A
{
CLASS_STUFF(A);
};
class B : public A
{
CLASS_STUFF(B);
};
class C
{
};
#include <iostream>
int main()
{
std::cout << IsPolymorphic<A>::value << std::endl;
std::cout << IsPolymorphic<B>::value << std::endl;
std::cout << IsPolymorphic<C>::value << std::endl;
std::cout << IsPolymorphic<int>::value << std::endl;
StartOfObject<A>::getStart(new A());
StartOfObject<B>::getStart(new B());
StartOfObject<C>::getStart(new C());
StartOfObject<int>::getStart(new int());
return 0;
};