'Specialize template function to return vector

Let's say I have a reader class over a file:

class Reader {
public:
    template <class T>
    T Read();
};

Its only function is the Read function that reads any arithmetic type (static_assert(std::is_arithmetic_v<T>)) from a file. Now I want to create a specialization of that function, which reads a vector from the file. How would I go about doing that with templates? Something like the following doesn't work:

template <class T>
std::vector<T> Read<std::vector<T>>();
error: function template partial specialization is not allowed
std::vector<U> Read<std::vector<U>>();
               ^   ~~~~~~~~~~~~~~~~


Solution 1:[1]

You can't partially specialize functions. You can overload them though, but the way of doing it is not obvious, since your function doesn't take any parameters.

First, you need a way to check if a type is a std::vector<??>:

template <typename T> struct IsVector : std::false_type {};
template <typename ...P> struct IsVector<std::vector<P...>> : std::true_type {};

Then you can plug it into requires:

template <typename T>
T Read()
{
    // Generic overload
}

template <typename T> requires IsVector<T>::value
T Read()
{
    // Vector overload
}

Alternatively, you could have a single function, with if constexpr (IsVector<T>::value) inside.

Solution 2:[2]

A way to implement what you want is to delegate the logic of your member function to a couple of private member functions:

#include <cstdio>
#include <vector>

class BinaryReader {
   public:
    template <class T>
    T Read() {
        T t{};
        this->ReadImpl(t);
        return t;
    }

private:
    template <class T>
    void ReadImpl(T& t) {
        static_assert(std::is_arithmetic_v<T>);
        std::puts("T");
        t = T{}; // implement your logic here
    }

    template <class T>
    void ReadImpl(std::vector<T>& t) {
        std::puts("std::vector<T>");
        t = std::vector<T>{}; // implement your logic here
    }
};

int main() {
    BinaryReader br;
    br.Read<int>();
    br.Read<std::vector<int>>();
}

This doesn't require you to introduce new type traits to check if your type is a std::vector<>. However, it requires your return types to be default constructible.

Output:

T
std::vector<T>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 HolyBlackCat
Solution 2 paolo