LeetCode 483: Smallest Good Base
Don't use std::uint_fast*_t
The performance gain of these types is minimal. If you really need to squeeze out the last bits of performance, it might help, but only if:

These types are actually different from the regular std::uint*_t
Implicit casts and integer promotions don't change the actual type used

The drawback is that the type names get very verbose. Also, can a std::uint_fast64_t actually hold an unsigned long long? The latter might very well be 128 bits long.
I suggest you stick with (unsigned) int when dealing with numbers that don't grow large (for example, for counting bits), size_t when you need to support arbitratily large counts, sizes and array indices, and other types as appropriate, such as unsigned long long to store the results of std::stoull().
Also use a using declaration to declare the type used to store the numbers in one place, so if you ever decide to change it, you don't have to search and replace all over the code. You can combine it with decltype() to get the type returned by an arbitrary function, like so:
class Solution {
    using value_type = decltype(std::stoull(&quot;&quot;));
    ...
};

Avoid unnecessary one-use temporaries
There are several cases where you define a temporary variable that is only used once, and which could be avoided. The first is x, which is only used to create a constant 1 of the right type. You can do that instead like so:
if ((value_type{1} &lt;&lt; bit) &lt; num) {
    ...
}

The second one will still technically create a temporary, but we can use C++17's if with an init statement:
if (auto curr = binarySearch(num, bit); curr) {
    return curr;
}

When passing an integer type to std::pow(), it will convert it to double for you, so you don't have to explcitly create a temporary constant dnum, and can just write:
value_type hi = std::pow(num, 1.0 / bit) + 1;

Note that you also don't need to explicitly cast it back to an integer here.