2

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";
}
6
  • 2
    Please post a minimal reproducible example, and also include the compiler error you get, verbatim. Commented Sep 10 at 11:33
  • 2
    how do you intend the compiler to pick the template that returns T or the one that returns Wrapper<T> when you call get_gadget<Foo> ? Forget about templates for a moment, then you are trying to overload based on return type alone, which is not possible Commented Sep 10 at 11:37
  • in other words, its not clear how you want your code to work. You have to clarify that before we can help fix it Commented Sep 10 at 11:37
  • suppose Wrapper<T> get_gadget(std::string_view str) would work, then auto underlying = get_get_gadget<T>(str); would just recursively call the same function again and again without end Commented Sep 10 at 11:39
  • If T is GadgetA or GadgetB, then use the specialization. Then if I write get_gadget<Wrapper<Foo>>(str), then this function is called ! Commented Sep 10 at 11:40

1 Answer 1

11

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
}
Sign up to request clarification or add additional context in comments.

6 Comments

turns out your interpretation is correct. Imho the question still should be clarified
alternatively one could specialize for T= Wrapper<U>. Either way it requires something extra, not sure which is nicer...
Specialize what, the function? They can't be partially specialized, only fully specialized.
Can concepts be used instead of type traits?
You can wrap WrapperTraits<T>::is_wrapper in a concept, but the class template isn't going anywhere, the concept would have to call it. The class template could be replaced with a bool variable template, but then you lose elem_type (which could work if Wrapper<T> has a member typedef pointing to T).
oh right .... sorry my confusion

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.