c++templates

Template specialization for container type


I would like to have separate specializations for a function when the type is a container vs. non-container types. For example:

template <typename T>
void print(const T& t) {
  cout << "non-container: " << t;
}

template <>
void print(const ContainerType& container) {
  cout << "container: ";
  for (const auto& t : container) {
    cout << t;
  }
}

Then I could use these functions like:

print(3); // Prints non-container: 3
print(vector{1, 1, 2}); // Prints container: 1 1 3
print(set{1, 1, 2}); // Prints container: 1 2

I don't know how to specify the specialization of print so that it will get called for vector and set but not for int. I don't want to have to create separate specializations for every container type I want a single specialization for any iterable type.


Solution

  • A quick version might be

    struct low_priority_tag {};
    struct high_priority_tag : low_priority_tag {};
    
    template <typename T>
    std::false_type is_container_impl(const T& t, low_priority_tag);
    
    template <typename T>
    auto is_container_impl(const T& container, high_priority_tag)
    -> decltype(std::begin(container), std::end(container), std::true_type{});
    
    template <typename T>
    using is_container = decltype(is_container_impl(std::declval<T>(), high_priority_tag{}));
    
    template <typename T>
    void print(const T& t) {
        if constexpr (is_container<T>::value) {
            std::cout << "container: {";
            const char* sep = "";
            for (const auto& e : t) {
                std::cout << sep;
                print(e);
                sep = ", ";
            }
            std::cout << "}";
        } else {
            std::cout << t;   
        }
    }
    

    Demo

    in C++20, with concept:

    template <typename T>
    concept Container = requires(T t)
    {
        std::begin(t);
        std::end(t);
    };
    
    template <typename T>
    void print(const T& t) {
        std::cout << t;   
    }
    
    template <Container C>
    void print(const C& c) {
        std::cout << "container: {";
        const char* sep = "";
        for (const auto& e : c) {
            std::cout << sep;
            print(e);
            sep = ", ";
        }
        std::cout << "}";
    }
    

    Demo