Результат преобразования указателя на функцию в другой указатель на тип функции - PullRequest
2 голосов
/ 11 декабря 2011

(5.2.10 / 6) C ++ 03 Указатель на функцию может быть явно преобразован в указатель на функцию другого типа.Эффект вызова функции через указатель на тип функции (8.3.5), который отличается от типа, используемого в определении функции, не определен.За исключением того, что преобразование значения типа «указатель на T1» в тип «указатель на T2» (где T1 и T2 являются типами функций) и обратно в его исходный тип дает исходное значение указателя, результат такого преобразования указателя не определен,[Примечание: см. Также 4.10 для получения более подробной информации о преобразованиях указателя.]

Вот что я пытаюсь сделать, хотя ясно, что результат преобразования fp1 в fp2создаст оригинальный указатель, но в то же время стандартная формулировка звучит так: "The result of such a pointer conversion is unspecified" Что это значит?

int f() { return 42; }

int main()
{
    void(*fp1)() = reinterpret_cast<void(*)()>(f);

    int(*fp2)() = reinterpret_cast<int(*)()>(fp1);

    // Safe to call the function ?
    fp2();
}

Ответы [ 3 ]

5 голосов
/ 11 декабря 2011

Да, это безопасно.
reinterpret_cast просто позволяет вам приводить один указатель к другому, но это не гарантирует никакой безопасности, и результат использования такого указателя не определен, если он не приведен обратно к исходному.тип.

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

Однако
reinterpret_cast гарантирует, что если вы приведете указатель с типом назад к исходному типу, указатель будет правильно сформирован.

Ваш код пытается выполнить второе, и, следовательно, это безопасно.

5 голосов
/ 11 декабря 2011

Вы неправильно читаете стандарт, «неопределенная» часть относится только к другим конверсиям:

За исключением [особого случая], результат такого преобразования указателя не указан.

В этом [особом случае] вы конвертируете обратно в исходный тип указателя на функцию, как в вашем примере. В этом особом случае преобразования дают исходное значение указателя, поэтому его можно использовать, как в вашем примере.

Только для других преобразований результат не указан.

4 голосов
/ 11 декабря 2011

Да, вы в безопасности.То, что вы делаете, охватывается случаем «За исключением этого преобразования ...».

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

enum CallbackType {
    eFuncPtrVoidReturningVoid,
    eFuncPtrVoidReturningInt,
    // ... more as needed ...
};

class CallbackRecord
{
public:
    CallbackRecord(void (*cb)()): cbType(eFuncPtrVoidReturningVoid), cbFunc(cb) 
        {}
    CallbackRecord(int (*cb)()): cbType(eFuncPtrVoidReturningInt), 
        cbFunc(reinterpret_cast<void (*)()>(cb)) {}
    void operator()() const;
protected:
    CallbackType cbType;
    void (*cbFunc)();
};

void CallbackRecord::operator()() const
{
    switch(cbType)
    {
    case eFuncPtrVoidReturningVoid:
        (*cbFunc)();
        break;

    case eFuncPtrVoidReturningInt:
        while((*reinterpret_cast<int (*)()>(cbFunc))())
            ;
        break;
    }
}

. Хотя вы можете просто сказать «сделать все обратные вызовы возвращаемыми int», для этого потребуется написать оболочки для всего, что не соответствует соглашению о вызовах, если числоиз типов обратного вызова выходит за рамки двух.Разрешение этих типов указателей на функции дает вам альтернативное средство поддержки нескольких типов обратного вызова и избавляет нас от необходимости превращать CallbackRecord в шаблон.Это также позволяет использовать подклассы или маршалинг для замены вышеприведенного оператора switch, не требуя использования методов virtual.

...