c ++编译时声明BASE是EXTENDED的基类并具有相同的内存地址

时间:2017-10-01 02:02:32

标签: c++ casting std

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;不感兴趣因为那个忽略了检查相同的内存地址。 还有另一种方法吗?

由于

2 个答案:

答案 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