是固定大小的成员指针和reinterpret_cast吗?

时间:2015-04-05 13:42:53

标签: c++ c++11 delegates standards-compliance reinterpret-cast

我正在尝试创建一个模板类,其中包含指向任意类实例和函数的指针,如下所示:

template<class C>
class A {
   typedef void (C::*FunctPtr)(); //e.g. void C::some_funct();

   FunctPtr functPtr_;
   C* instPtr_;
public:
   A(FunctPtr functPtr, C* instPtr)
      : functPtr_(functPtr)
      , instPtr_(instPtr) {}
};

但是,我希望能够使用placement new创建此类的实例,而无需动态内存分配。 C ++标准是否保证此模板类对于所有类C具有固定大小?

在指针的Don Clugston's article中,我注意到各种编译器上的成员函数指针的各种大小的图表,并且一些编译器的大小并不总是相同。我以为我被软管了但这个标准是否合规?从C ++标准秒。 5.2.10关于重新解释演员:

- 将“指向成员函数的指针”类型的prvalue转换为指向成员函数的不同指针 键入并返回其原始类型会生成指向成员值的原始指针。

C ++标准中的该语句是否表明成员函数指针的大小都相同?

如果不是,我想我仍然可以按如下方式重写代码,以明确地利用reinterpret_cast保证:

class GenericClass;

template<class C>
class A {

   typedef void (GenericClass::*GenFunctPtr)();
   typedef void (C::*SpecificFunctPtr)();

   GenFunctPtr functPtr_; //store any kind of function ptr in this fixed format
   GenericClass* instPtr_;

public:
   A(SpecificFunctPtr functPtr, C* instPtr)
      : functPtr_(reinterpret_cast<GenFunctPtr>(functPtr))
      , instPtr_(reinterpret_cast<GenericClass*>(instPtr)) {}

   void DoSomething()
   {
      //now convert pointers back to the original type to use...
      reinterpret_cast<SpecificFunctPtr>(functPtr_);
      reinterpret_cast<C*>(instPtr_);
   }
};

现在似乎需要大小相同但符合标准,对吧?我更喜欢第一种选择,但如果我必须第二种选择也会有效。想法?

2 个答案:

答案 0 :(得分:0)

我不知道是否在标准中指定了指向成员的指针的实现细节(找不到它),但是因为我们知道C*将具有相同的大小C 1}},我们可以为各种类型FunctPtr确定C的大小,只需添加static_assert

template <class C>
class A {
   typedef void (C::*FunctPtr)(); //e.g. void C::some_funct();
   static_assert(sizeof(FunctPtr) == 16, 
       "unexpected FunctPtr size"); // from coliru, clang and gcc

   FunctPtr functPtr_;
   C* instPtr_;
   ...
};

至少我尝试过几种类型的类({base,derived,multiple derived} x {virtual,non-virtual}),它总是给出相同的大小。

这可能是平台和/或编译器特定的,因为Pointers to member functions are very strange animals表示:

  

仅使用单继承的类的指向成员函数的大小就是指针的大小。
  使用多重继承的类的指向成员函数的大小是指针的大小加上size_t的大小。

这不是我在clang或gcc中看到的。

答案 1 :(得分:0)

Microsoft编译器使用不同大小的成员指针,具体取决于类的复杂程度。从技术上讲,这是不合规的(因为你可以定义指向成员的指针,即使在类定义不可见的情况下),但它在实践中运行得非常好,默认情况下也是如此。 Pragma和编译器开关可用于控制此行为。请参阅示例https://msdn.microsoft.com/en-us/library/83cch5a6.aspx

即使如此,任何给定类的指针总是大小相同,只是不同的类可能有不同大小的成员指针。