Reputation: 7616
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.
Upvotes: 1
Views: 369
Reputation: 1835
Since C++20, it is possible to constraint the second function overload with the concept std::ranges::input_range
. It is essentially a refinement of range for which the std::ranges::begin()
function returns a model of std::input_iterator
.
Example:
template <typename T>
void print(const T& t)
{ std::cout << "non-container: " << t; }
template <std::ranges::input_range T>
void print(const T& x) {
std::cout << "container: ";
for (const auto& t : x)
std::cout << t;
}
Upvotes: 0
Reputation: 217135
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;
}
}
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 << "}";
}
Upvotes: 5