Общий указатель метода.reinterpret_cast указатель метода на другой класс, это UB? - PullRequest
1 голос
/ 08 июля 2019

Является ли это нарушением стандарта C ++ (любой его версии) каким-либо образом для повторного интерпретации из одного метода класса в совершенно другой указатель метода класса, если ни один из классов не является производным от другого или каким-либо образом связан, если указательне разыменовывается до тех пор, пока он не будет переинтерпретирован обратно в соответствующий объект класса?

Отличаются ли типы разных указателей методов класса в какой-либо реализации стандарта?(Например, возможно, если один класс является полиморфным, а другой - нет)

В этом посте говорится, что использование 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 ]

2 голосов
/ 09 июля 2019

Стандарт гарантирует поведение, о котором вы спрашиваете: (C ++ 17 [expr.reinterpret.cast] / 10)

Значение типа «указатель на член X типа T1»Может быть явно преобразовано в значение другого типа« указатель на член Y типа T2 », если T1 и T2 оба являются функциональными типами или обоими типами объектов.Значение указателя с нулевым членом преобразуется в значение указателя с нулевым элементом целевого типа.Результат этого преобразования не определен, за исключением следующих случаев:

  • преобразование значения типа «указатель на функцию-член» в другой указатель на тип функции-члена и обратно в его исходный тип даетисходный указатель на значение члена.
  • [...]

Так что да, вы можете reinterpret_cast одна функция указателя на член для другой в целяхсохраняя его, а затем извлекая исходное значение с помощью обратного приведения.

Конечно, вы не можете попытаться вызвать функцию через промежуточный указатель на член.

1 голос
/ 08 июля 2019

Насколько я знаю, уловки с прямыми вызовами метода без контекста класса не определены стандартом.Кроме того, они строго не рекомендуются, потому что это зависит от компилятора, платформы и т. Д.
На самом деле, в зависимости от вашей задачи, вы можете использовать переменные шаблоны для вызова любого метода с любыми параметрами, например:

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.Но как насчет возвращения пустоты?Для этого нам нужна отдельная специализация шаблона:

// 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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...