0
votes

I have a template class and a function with a template return type:

template<typename T>
class Wrapper {
public:
    Wrapper(const T& _data) : data(_data) { }

    const T& get_data() {return data;};
private:
    T data;
};

template <typename T>
Wrapper<T> build_wrapped(){
    T t{};
    return Wrapper<T>(t);
}

Suppose that I want to extend the returned Wrapper for one specific type T and specialise the build_wrapped() function for the type. So, let's create a template to hold the return type and use it in build_wrapped():

template<typename T>
struct ReturnType {
    using type = Wrapper<T>;
};

template <typename T>
typename ReturnType<T>::type build_wrapped(){
    T t{};
    return typename ReturnType<T>::type(t);
}

And use it to provide the specialization:

struct Foo{};

class ExtendedWrapper : public Wrapper<Foo> {
public:
    ExtendedWrapper(const Foo& _data) : Wrapper(_data) {}
    int get_answer() {return 42;};
};

template<>
struct ReturnType<Foo> {
    using type = ExtendedWrapper;
};

template<>
typename ReturnType<Foo>::type build_wrapped(){
    Foo t{};
    return typename ReturnType<Foo>::type(t);
}

However, the final declaration is rejected by both gcc and clang. For example, clang returns:

error: no function template matches function template specialization 'build_wrapped'

note: candidate template ignored: couldn't infer template argument 'T'

I can get around using ReturnType by creating an explicit specialisation of Wrapper for Foo. However, this requires duplicating all the code in Wrapper (in my real life case it's a substantial class), when I just want to add some new functionality (as in ExtendedWrapper). So, can this be done? What's wrong with the approach above?

1
One alternative is to have tag and then use overload:: template <typename T> struct tag{}; template <typename T> Wrapper<T> build_wrapped(tag<T>) { return Wrapper<T>{{}};} ExtendedWrapper build_wrapped(tag<Foo>) { return ExtendedWrapper{{}};}.Jarod42

1 Answers

4
votes

The compiler already told you what the problem is:

note: candidate template ignored: couldn't infer template argument 'T'

You need to specify the template parameter explicitly.

template <>
typename ReturnType<Foo>::type build_wrapped<Foo>()
{ //                                        ^~~~~
    Foo t{};
    return typename ReturnType<Foo>::type(t);
}