Конкретно в вашем случае нет нужды извращаться, перегрузка функций — самое лучшее.
А так здесь есть два вопроса.
1. А что напишем в скобках вместо непонятного return?
2. В какой код оно должно скомпилироваться?
Пример стандартный шаблонный.
template <class Device>
SomeFunction getProcAddress(const Device& device, const std::string& name);
template <>
inline SomeFunction getProcAddress<VkInstance>(
const VkInstance& device, const std::string& name)
{ return vkGetInstanceProcAddr(device, name.c_str()); }
Это стандартное использование шаблонов Си++ с далёкого 1998 года.
Пример через if constexpr. Си++17, значит.
template <class Device>
inline SomeFunction getProcAddress(const Device& device, const std::string& name)
{
if constexpr (std::is_same_v<Device, VkInstance>) {
return vkGetInstanceProcAddr(device, name.c_str());
} else {
// false нельзя! — проверка произойдёт в любом случае, будет компилироваться ветка или нет.
static_assert(std::always_false_v<Device>, "Unknown type");
}
}
Используется в функции std::visit, чтобы исполнить разный код для разных вариантов std::variant и ничего не забыть.
Что написано в скобках — ну, сами видите. Во что скомпилируется — в две разных функции.
В некоторых случаях — вероятно, не в вашем — можно ещё поиграться с виртуальным полиморфизмом.