通用方法指针。 reinterpret_cast方法指向不同类的指针,这是UB吗?

时间:2019-07-08 16:09:23

标签: c++ pointers reinterpret-cast

如果两个类都不是从另一个类派生或以任何方式关联的,则它以任何方式违反C ++标准(它的任何版本)从一个类方法重新解释_cast到一个完全不同的类方法指针,只要该指针直到被重新解释回正确的类对象才被取消引用?

在任何标准实现中,不同类方法指针的类型是否不同? (例如,也许一类是多态的,而另一类不是多态的)

这篇文章说,reinterpret_cast的用法遵循以下标准:https://stackoverflow.com/a/573345

这篇文章说,从方法指针到函数指针的转换是UB,这不是我正在做的,但是在理论上是相似的:https://stackoverflow.com/a/6908641

编辑:我需要运行时通用性,因为我要持有这些指针的向量,并且由于模板是在编译时求值的,因此效果不佳。我有点需要颠覆类型系统以持有通用指针,并知道当我将其强制转换回原始类型时,它将不会被破坏。

我要实现的伪代码:

// A type for Foo method pointer
typedef bool (Foo::*FOO_METHOD_PTR)( int some_arg );

// A generic method pointer type
class UnusedClass {};
typedef void (UnusedClass::*GENERIC_METHOD_PTR)( void );

// generically store a method from any class
GENERIC_METHOD_PTR generic_ptr = reinterpret_cast<GENERIC_METHOD_PTR>( &Foo::method );

// cast back to proper class
FOO_METHOD_PTR foo_ptr = reinterpret_cast<FOO_METHOD_PTR>( generic_ptr );

// macro to help call method pointers
#define CALL_METHOD(object,ptrToMethod)  ((object).*(ptrToMethod))

// call
Foo foo;
CALL_METHOD(foo,foo_ptr)

2 个答案:

答案 0 :(得分:2)

该标准确实可以保证您要求的行为:(C ++ 17 [expr.reinterpret.cast] / 10)

  

如果T1和T2都是函数类型或都是对象类型,则可以将“指向T1类型的X成员的指针”类型的prvalue显式转换为“指向T2类型的Y成员的指针”类型的prvalue。 。空成员指针值将转换为目标类型的空成员指针值。除以下情况外,未指定此转换的结果:

     
      
  • 将类型为“指向成员函数的指针”的prvalue转换为指向成员函数类型的不同指针,然后再返回其原始类型将产生指向成员值的原始指针。
  •   
  • [...]
  •   

是的,您可以将一个指向成员函数的指针reinterpret_cast传递给另一个函数以进行存储,然后使用强制类型转换的逆函数来检索原始值。

当然,您不能尝试通过中间指向成员的指针来调用函数。

答案 1 :(得分:1)

据我所知,标准中未指定直接调用不带类上下文的方法的技巧。另外,也不建议使用它们,因为它取决于编译器,平台等。
实际上,根据您的任务,您可以使用可变参数模板来调用具有任何参数的任何方法,例如:

template<typename TClass, typename TResult, typename ... Type>
struct SmartCall {
  // call for non-const class methods
  static TResult Invoke(TClass* context, TResult (TClass::*method)(Type...), Type... args) {
    return (context->*(method))(args ...);
  }

  // call for const class methods
  static TResult Invoke(TClass* context, TResult (TClass::*method)(Type...) const, Type... args) {
    return (context->*(method))(args ...);
  }
};

很好,您可以将其用于任何返回void以外内容的类的const和非const方法调用。但是返回虚空呢?我们需要对其进行单独的模板专门化:

// SmartCall specialization for 'void' return type
template<typename TClass, typename ... Type>
struct SmartCall<TClass, void, Type...> {
  static void Invoke(TClass* context, void (TClass::*method)(Type...), Type... args) {
    (context->*(method))(args ...);
  }
  static void Invoke(TClass* context, void(TClass::*method)(Type...) const, Type... args) {
      return (context->*(method))(args ...);
  }
};

用法示例:

    std::string a = "Hello World";

    // same as    size = a.size();
    size_t size =  SmartCall<std::string, size_t>::Invoke(&a, &std::string::size);

    // same as    symbol_w_pos = a.find_first_of("W", 0);
    size_t symbol_w_pos = SmartCall<std::string, size_t, const char*, const std::size_t>::Invoke(&a, &std::string::find_first_of, "W", 0);

    // same as    a.clear();
    SmartCall<std::string, void>::Invoke(&a, &std::string::clear);