Since B is a child of A, B is A,
Not quite. Since B is a child of A, any B object is also an A object. However, this is not symmetric, so it is overstating the case to claim that B is A. It is correct to claim "a B is an A", but dropping the indefinite articles ("a" and "an") makes the claim false.
why is a function pointer from "void to B" isn't like a function pointer from "void to A"?
Because B is not A. The return types are different, so the function types are different. A B object can be converted to an A object (by copying the A sub-object), but that is an extra step. If you were to write
A value = B::boo();
then the compiler inserts a conversion from B to A after boo() is called. The conversion might not require machine instructions (a.k.a. a "no-op"), but it is there nonetheless. (One side-effect of having a conversion is that copying cannot be elided.)
A value = static_cast<A&&>(B::boo());
If you were to invoke boo() through a function pointer of the type A(*)(), there would be nothing to tell the compiler to insert the conversion. Skipping the conversion could be an issue, particularly if a B object had data members not in A objects, especially if those data members had non-trivial destructors.
The language does not attempt to draw a line between the cases where skipping the conversion is and is not a problem. This applies to both the return type and the parameter types. The type of a function assigned to a pointer must match the pointer's type exactly, no conversions needed.
This is where function objects, like std::function, are useful. A std::function<A()> could store either A:foo or B::boo. This is because the function object will adapt and record what conversions are needed, similar to what happens when invoking a function directly. Unlike a function pointer, a function object does not require an exact match on types.