Search code examples
c++templatestemplate-meta-programmingsfinae

Allow scalar and vector types in a template


I am working on a class template for a signal processing library that is supposed to hold scalar samples or vectors of scalar samples (which is common in, e.g., frame-wise signal processing).

The class is essentially a facade for the std::vector with some added convenience methods and members.

For scalar types, everything is fine. But I am running into trouble when I allow vectors as template arguments. Here is a minimum example of what I mean:

template <class T>
class Signal
{
    public:
        Signal() = default;
        std::vector<T> samples;
           
        friend std::ostream& operator<<(std::ostream& os, const Signal& obj)
        {
            for (const auto& x : obj.samples)
            {
                os << x << ",";
            }
            return os << std::endl;
        }
}

I am obviously getting an error about a missing '<<' operator for a right-hand operand of type std::vector. What I want is to define different operators (and also some other methods not shown in the minimum example above) for scalar and vector-like template arguments.

I have done some research and learned about the concept of SFINAE, but I am having trouble connecting the dots.

Can anyone point me in the right direction? I feel like this should be a fairly common problem. Or am I going about this all wrong?


Solution

  • You might use overloads instead of SFINAE:

    template <typename T>
    std::ostream& print(std::ostream& os, const T& obj)
    {
        return os << obj;
    }
    
    template <typename T>
    std::ostream& print(std::ostream& os, const std::vector<T>& v)
    {
        os << "{";
        const char* sep = "";
        for (const auto& e : v) {
            os << sep;
            print(os, e);
            sep = ", ";
        }
        return os << "}";
    }
    
    template <class T>
    class Signal
    {
    public:
        Signal() = default;
        std::vector<T> samples;
           
        friend std::ostream& operator<<(std::ostream& os, const Signal& obj)
        {
            for (const auto& x : obj.samples)
            {
                print(os, x) << ",";
            }
            return os << std::endl;
        }
    };
    

    Demo