Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Template Function Ambiguity for Wrapper Type Specialization

Tags:

c++

c++23

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";
}
like image 529
Roger World Avatar asked Jul 29 '26 01:07

Roger World


1 Answers

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
}
like image 147
HolyBlackCat Avatar answered Jul 31 '26 16:07

HolyBlackCat



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!