1

This can be compiled (despite being UB (right?) because fvp == nullptr)

int f;
void* fvp{};
decltype(f)* fp = static_cast<decltype(f)*>(fvp);

but this cannot

void f() {}
void* fvp{};
decltype(f)* fp = static_cast<decltype(f)*>(fvp);

because (Clang says)

Static_cast from 'void *' to 'decltype(f) ' (aka 'void ()()') is not allowed [bad_cxx_cast_generic]

Why is this the case? And where from the standard do I understand this? I guess [expr.static.cast] is where I should look at; specifically 7, but I'm not sure.


Follow up question on the reason why I wanted to ask this one.

Enlico
  • 23,259
  • 6
  • 48
  • 102
  • 1
    Because pointers to functions aren't guaranteed to be convertible to `void*`. Basically, there is no guarantee for a full-round trip between `void*` and a function pointer. – Jason Oct 09 '22 at 06:55
  • @JasonLiam I don't doubt your right, but I don't see _why_ that is the case. – Enlico Oct 09 '22 at 07:00
  • Because code pointers and data pointers are not guaranteed to be the same size. – Zastai Oct 09 '22 at 07:10

2 Answers2

3

This is not allowed because [expr.static_cast] does not allow it. Indeed, the relevant language is:

No other conversion shall be performed explicitly using a static_­cast.

Since there is no conversion listed that would permit conversion from void* to a function pointer type, that conversion is not allowed.

The rationale is that function and data pointers may have different sizes or alignment requirements.

You might consider a reinterpret_cast, which conditionally supports such conversions. But it would be better to use a design that does not require such conversions. If you need to type-erase a function pointer, you will still need reinterpret_cast but can use e.g. void(*)() as the erased type, which is always supported.

ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • Since the list below your quote is fairly long and uses the word _function_ several times, I'd be curious to ask: what would be written there if this conversion was allowed? Something like _"`void*`-to-pointer-to-function conversion"_? – Enlico Oct 09 '22 at 08:01
  • 1
    The language would be "object pointer to function pointer" or vice-versa, as in the specification of reinterpret_cast. – ecatmur Oct 09 '22 at 08:10
1

where from the standard do I understand this? I guess [expr.static.cast] is where I should look at; specifically 7, but I'm not sure

Basically, there is no guarantee for a full-round trip between void* and a function pointer.

This can be seen from expr.reinterpret.cast#8 which says:

Converting a function pointer to an object pointer type or vice versa is conditionally-supported. The meaning of such a conversion is implementation-defined, except that if an implementation supports conversions in both directions, converting a prvalue of one type to the other type and back, possibly with different cv-qualification, shall yield the original pointer value.

Jason
  • 36,170
  • 5
  • 26
  • 60
  • I would have thought that the verb _to convert_ in this paragraph referred to `reinterpre_cast`, given the name of the section. I guess I'm wrong. (Not that I had read it before asking the question, tbh.) – Enlico Oct 09 '22 at 07:20
  • 1
    Doesn't it refer exclusively to `reinterpret_cast`? – HolyBlackCat Oct 09 '22 at 07:45
  • @HolyBlackCat Yes, but my point is that the round-trip is not allowed so i quoted that part. – Jason Oct 09 '22 at 07:46
  • @JasonLiam, doesn't it refer exclusively to `reinterpret_cast`-based round trip? – Enlico Oct 09 '22 at 07:48
  • I don't entirely understand what you're trying to say. The bold part is likely intended to support rare platforms with separate code and data memory. – HolyBlackCat Oct 09 '22 at 07:50
  • 1
    @Enlico As you said in your first comment, I think that the use of the verb `convert` here is meant to be general. Otherwise they could've wrote something like: *"`reinterpret-cast` a function pointer to an object pointer type or vice versa is conditionally-supported."* That is my understanding(which may be wrong). – Jason Oct 09 '22 at 07:53
  • I bet OP's compiler would accept a `reinterpret_cast` in this scenario. – HolyBlackCat Oct 09 '22 at 07:56