struct A
{
};
struct B : A
{
virtual ~B() {}
};
template<typename BASE, typename EXTENDED>
void ASSERT_BASE_EXTENDED()
{
static_assert(static_cast<BASE*>((EXTENDED*)256)==(BASE*)256, "error");
}
我正在寻找一种方法来进行编译时断言以检查BASE类是否是EXTENDED的基础,并且它们具有相同的内存地址。
在上面的例子中,即使B基于A,当转换为A时它有不同的内存地址,因为虚函数表指针实际上是B的第一个成员。但我需要检查A是否是第一个构件。
以上工作正常,但不是编译时,因为我收到错误&#34;错误C2131:表达式没有评估为常量&#34;使用VS 2017编译器时。
我对&#34; std :: is_base_of&#34;不感兴趣因为那个忽略了检查相同的内存地址。 还有另一种方法吗?
由于
答案 0 :(得分:1)
内存地址是运行时构造。您无法在编译时检查它们,因为此时它们不存在。你提到的那种铸造也是如此。这完全发生在运行时。您必须使用运行时检查和错误处理来替换static_assert
,例如assert()
或例外。
这是针对imo常见用例的。对于像您的示例中那样的硬编码内存地址,问题在于将这些地址转换为指针。执行此操作的唯一有效方法是reinterpret_cast(这是编译器在您的示例中为C样式转换而尝试的转换之一),因为类型 int 和指向T的指针完全不相关。但是在编译时不允许使用reinterpret_cast。
Clang的错误消息很好地说明了这一点:
main.cpp:14:38: note: cast that performs the conversions of a reinterpret_cast is not allowed in a constant expression
static_assert(static_cast<BASE*>((EXTENDED*)256)==(BASE*)256, "error");
^
答案 1 :(得分:0)
限制:它仅适用于具有constexpr构造函数的类
class Base_A
{
size_t a;
};
class Base_B
{
size_t b;
};
class Derived2: public Base_A, public Base_B
{
size_t x;
};
template<class Derived, class Base>
constexpr bool IsSameAddressCast()
{
Derived drv;
Derived* drv_p = &drv;
Base* base_p = drv_p;
void* drv_v = drv_p;
void* base_v = base_p;
return (drv_v==base_v);
};
static_assert(IsSameAddressCast<Derived2, Base_A>(), "different address");
static_assert(IsSameAddressCast<Derived2, Base_B>(), "different address");// this assert will triggers
另一种解决方案假设您可以访问基类中的一些非静态成员
class Base_A
{
public:
size_t amember;
};
class Base_B
{
size_t b;
};
class Derived1: public Base_B, public Base_A
{
size_t x;
};
class Derived2: public Base_A, public Base_B
{
size_t x;
};
class Derived3: public Base_B
{
public:
size_t amember;
};
template<class T>
struct Allocator_OffsetOf_ObjData
{
static const size_t value = (size_t)&(((T*)nullptr)->amember);
};
template<class Derived, class Base>
constexpr bool IsSameAddressCast2()
{
static_assert(std::is_base_of<Base, Derived>::value, "not a base class");
return Allocator_OffsetOf_ObjData<Base>::value == Allocator_OffsetOf_ObjData<Derived>::value;
}
static_assert(IsSameAddressCast2<Derived1, Base_A>(), "different base address");// triggers assert
static_assert(IsSameAddressCast2<Derived2, Base_A>(), "different base address");
static_assert(IsSameAddressCast2<Derived3, Base_A>(), "different base address");// triggers assert