I was thinking a lot what title to put on my question and failed anyway, so if you find a good one, please edit it.
I am trying to write a print function for a vector or other container<T> and have another print function for container<container<T>>, so here what I came up with:
template<typename T>
void print(T const& cont){
for (const auto& i : cont) {
cout << i << " ";
}
cout << endl;
}
template<typename T, template<typename> typename Cont>
void print(Cont<T> const& cont) {
for (const auto& i : cont) {
print(i);
}
}
and I have my 2 target containers here:
vector<vector<int>> subsets;
vector<int> subset;
When I invoke print(subset); the program works as expected, but when i invoke print(subsets), compiler starts complaining:
error C2679: binary '<<': no operator found which takes a right-hand operand of type 'const std::vector<int,std::allocator<int>>' (or there is no acceptable conversion)
My conclusion is that its still trying to call the non-nested template print function and failing on cout as I am trying to cout a vector.
Can anyone explain why the overload resolution is not working as I expect and what I did wrong here? Even when I rename the nested-template function to printn, its started to complain for a different reason:
error C2784: 'void prints(const Cont<T> &)': could not deduce template argument for 'const Cont<T> &' from 'std::vector<std::vector<int,std::allocator<int>>,std::allocator<std::vector<int,std::allocator<int>>>>'
The short, simple and insufficient answer is that std::vector has 2 template parameters. You should also include some spacing:
template<class T, class A, template<class, class>class C>
void print(C<T,A> const& cont) {
std::cout << "[ ";
bool bFirst = true;
for (const auto& i : cont) {
if (!bFirst)
std::cout << ", ";
bFirst = false;
print(i);
}
std::cout << " ]";
}
So that overload was never called.
Once you do this, your code doesn't work, because you don't have an element-printer. So replace your other loop-printer with an element-printer:
template<typename T>
void print(T const& i){
std::cout << i;
}
Live example.
Test code:
std::vector<int> a={1,2,3};
print(a);
std::cout << "\n";
std::vector<std::vector<int>> b = {a, a, a};
print(b);
std::cout << "\n";
Output:
[ 1, 2, 3 ]
[ [ 1, 2, 3 ], [ 1, 2, 3 ], [ 1, 2, 3 ] ]
this is insufficient, because you really should do something fancier to detect "is this object iterable" and "is this object tuple-like" if you want a more serious general purpose printer. Detecting Cont<A,B> templates is a poor substitute.
Here is code to detect if something is iterable (ignore the bad checked answer, read the one I linked to).
Then do a SFINAE test for "is the argument iterable" in print for the one that does a for(:) loop.
The next thing you'll want to do is detect if the object is tuple-like. If it is, you want to print out each element of the tuple. This give you std::map and std::unordered_map support. Note that a std::array is both tuple-like and iterable.
This is a bit harder than detecting "iterable", and varies more with which C++ standard you are working with, because tuple-like-ness is expanding with new versions of C++. You can be lazy and just detect std::pair and std::tuple, which will cover 99% of use cases.
Your issue here is that std::vector has more than one template type. Because of this, the T overload is use instead. The reason this happens is from an ambiguity in the language on how default template parameters should be considered in template template parameter. This led to the defect report DR150 which was adopted in C++17 and will allow your code to work in a compliant compiler1. To work around this you can use a variadic template template parameter and adjust the base case to just print the element like
template<typename T>
void print(T const& elem){
cout << elem << " ";
}
template<template<typename...> typename Cont, typename... Params>
void print(Cont<Params...> const& cont) {
for (const auto& i : cont) {
print(i);
}
cout << endl;
}
int main()
{
vector<vector<int>> subsets{{1,2},{3,4}};
print(subsets);
}
outputs
1 2
3 4
1: I had to adjust the base case like in my example because now the template temaplte version will call the nested vector
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With