I have a C++ function template:
#include <string_view>
template<typename T>
T get_gadget(std::string_view str);
I also added explicit specializations for some user-defined types, e.g., GadgetA and GadgetB:
template<>
GadgetA get_gadget<GadgetA>(std::string_view str) {
// implementation
}
template<>
GadgetB get_gadget<GadgetB>(std::string_view str) {
// implementation
}
Now, I want to make it work for a Wrapper type like this:
template<typename T>
Wrapper<T> get_gadget(std::string_view str) {
auto underlying = get_gadget<T>(str);
// implementation
}
However, when I compile, I get an ambiguous call error. I think this happens because the compiler cannot distinguish between the primary template and the Wrapper specialization.
I would like to solve this using concepts or template metaprogramming, without using void pointers, tag dispatching, or unnecessary boilerplate.
#include <iostream>
#include <string_view>
// Primary template (only declared)
template<typename T>
T get_gadget(std::string_view str);
// --- Some user-defined types ---
struct GadgetA {
std::string name;
};
struct GadgetB {
int value;
};
// --- Explicit specializations ---
template<>
GadgetA get_gadget<GadgetA>(std::string_view str) {
return GadgetA{std::string(str)};
}
template<>
GadgetB get_gadget<GadgetB>(std::string_view str) {
return GadgetB{static_cast<int>(str.size())};
}
// --- A generic Wrapper ---
template<typename T>
struct Wrapper {
T inner;
};
// --- Generic overload for Wrapper<T> ---
template<typename T>
Wrapper<T> get_gadget(std::string_view str) {
auto underlying = get_gadget<T>(str); // delegate
return Wrapper<T>{underlying};
}
// --- Demo ---
int main() {
auto a = get_gadget<GadgetA>("HelloA");
auto b = get_gadget<GadgetB>("HelloB");
auto wa = get_gadget<Wrapper<GadgetA>>("WrappedA");
std::cout << "GadgetA: " << a.name << "\n";
std::cout << "GadgetB: " << b.value << "\n";
std::cout << "Wrapper<GadgetA>: " << wa.inner.name << "\n";
}
I'm going to assume that the intended call syntax for last overload is get_gadget<Wrapper<X>>("...") (not get_gadget<X>("..."), which would make no sense, as noted in the comments).
In that case, the T template parameter of that function will be Wrapper<X>, not X.
So you need something like:
template <typename T>
struct WrapperTraits
{
static constexpr bool is_wrapper = false;
};
template <typename T>
struct WrapperTraits<Wrapper<T>>
{
static constexpr bool is_wrapper = true;
using elem_type = T;
};
And then:
template <typename T> requires WrapperTraits<T>::is_wrapper
T get_gadget(std::string_view str)
{
auto underlying = get_gadget<typename WrapperTraits<T>::elem_type>(str);
// implementation
}