Это тангенциальное продолжение моего предыдущего вопроса Адрес функции, соответствующей перегрузке bool vs const void *. Ответчик объяснил:
Стандарт [С++ 11] не определяет стандартные преобразования из "указатель на функцию" на "указатель на
void
".Трудно представить цитату из-за отсутствия чего-то, но ближайший я могу сделать это С++ 11 4.10/2 [conv.ptr]:
Значение типа "указатель на cv
T
", , гдеT
является типом объекта, может быть преобразовано в значение prvalue типа "указатель на cvvoid
". Результат преобразования указателя на cvT
"на " указатель на cvvoid
" указывает на начало места хранения где объект типаT
находится, как если бы объект был наиболее (1.8) типа T (т.е. не подобъект базового класса). Значение нулевого указателя преобразуется в значение нулевого указателя тип назначения.(акцент мой)
Предполагая, что func
объявлен void func();
, если вы выполняете C-стиль, т.е. (void*) func
, приведение будет успешным. static_cast<void*>(func)
однако недействителен, но reinterpret_cast<void*>(func)
будет успешным. Однако вы не можете сделать преобразованный преобразованный указатель обратно в исходный. Например,
Fine:
int main() {
int* i;
void* s = static_cast<void*>(i);
i = static_cast<int*>(s);
s = reinterpret_cast<void*>(i);
i = reinterpret_cast<int*>(s);
}
Неплохо:
void func() { }
int main() {
void* s = reinterpret_cast<void*>(func);
reinterpret_cast<decltype(func)>(s);
}
N3337 начинается, говоря:
[expr.reinterpret.cast]
Результат выражения
reinterpret_cast<T>(v)
является результатом преобразуя выражениеv
в типT
. ЕслиT
- значение lvalue ссылочного типа или ссылки на тип функции, результат lvalue; еслиT
является ссылкой rvalue на тип объекта, результатом является значение x; в противном случае результатом является значение prvalue и значение lvalue-to-rvalue (4.1), от массива к указателю (4.2) и стандартного стандарта (4.3) преобразования выполняются над выражением v. Конверсии, которые могут быть выполняемые явно с использованиемreinterpret_cast
, перечислены ниже. нет другое преобразование может быть выполнено явно с помощьюreinterpret_cast
.
Я выделил язык, который, по моему мнению, является ключевым. Последняя часть, по-видимому, подразумевает, что если конверсия отсутствует в списке, она является незаконной. Вкратце, разрешенные преобразования:
- Указатель может быть явно преобразован в любой целочисленный тип, достаточно большой для его хранения.
- Значение типа интегрального типа или перечисления может быть явно преобразовано в указатель.
- Указатель функции может быть явно преобразован в указатель функции другого типа.
- Указатель объекта может быть явно преобразован в указатель объекта другого типа.
- Преобразование указателя функции в тип указателя объекта или наоборот условно поддерживается.
- Значение нулевого указателя (4.10) преобразуется в значение нулевого указателя для типа назначения.
- Значение prawue типа "указатель на член
X
типаT1
" может быть явно преобразовано в prvalue другого типа "указатель на элемент Y типаT2
", еслиT1
иT
2 - оба типа функций или оба типа объектов. - Выражение lvalue типа
T1
может быть передано типу "ссылка наT2
", если выражение типа "указатель наT1
" может быть явно преобразовано в тип "указатель наT2
" используя reinterpret_cast.
void*
не является указателем на функцию, а объекты не имеют функции или типа void.
[basic.types]
Тип объекта - это (возможно, cv-квалифицированный) тип, который не является тип функции, а не тип ссылки, а не тип void.
Так что, возможно, я хватаюсь за соломинку, но кажется, что reinterpret_cast<void*>(func)
является незаконным. Однако, с другой стороны, [expr.static.cast]/5 говорит: "В противном случае static_cast
должен выполнить одно из перечисленных ниже преобразований.
выполняется явным образом с помощью static_cast
. "Ключевое различие заключается в том, что" должен "и" может ". Достаточно ли сделать законный reinterpret_cast
закон или я пропущу что-то еще?