Я пытаюсь написать функцию, которую я могу вызвать всякий раз, когда мне нужно что-то напечатать. Однако при печати вектора или списка мне нужно, чтобы он вел себя немного иначе. Кроме того, вектор или список может содержать другой вектор или список.
Мой первый подход был сделать что-то вроде этого:
#include <iostream>
#include <list>
#include <vector>
using namespace std;
template <typename T>
void
print(const vector<T>& v) {
cout << "[";
bool isFirst = true;
for (const T& e : v) {
if (isFirst) isFirst = false;
else cout << ",";
print(e);
}
cout << "]";
}
template <typename T>
void
print(const list<T>& l) {
cout << "[";
bool isFirst = true;
for (const T& e : l) {
if (isFirst) isFirst = false;
else cout << ",";
print(e);
}
cout << "]";
}
template <typename T>
void
print(const T& v) {
cout << v;
}
int
main(int argc, char** argv) {
vector<int> v;
print(v);
return 0;
}
Как видите, для печати векторов и списков существует много дубликатов кода, но я не знаю, как их объединить. В любом случае код не компилируется, так как компилятор пытается сопоставить печать скалярного объекта (например, int
с первой реализацией print
вместо последней реализации:
g++ -std=c++11 test.cpp
test.cpp: In instantiation of ‘void print(const std::vector<T>&) [with T = int]’:
test.cpp:42:12: required from here
test.cpp:15:16: error: no matching function for call to ‘print(const int&)’
print(e);
^
test.cpp:15:16: note: candidate is:
test.cpp:9:1: note: template<class T> void print(const std::vector<T>&)
print(const vector<T>& v) {
^
test.cpp:9:1: note: template argument deduction/substitution failed:
test.cpp:15:16: note: mismatched types ‘const std::vector<T>’ and ‘const int’
print(e);
^
Есть идеи, как мне это решить?
Есть много опрятных решений, но одно, которое очень близко к вашему без дублирования, без чего-либо слишком умного и без какой-либо библиотечной функции, заключается в следующем
template <typename T>
void print_container(const T&);
template <typename T>
void print(const std::vector<T>& v) { print_container(v); }
template <typename T>
void print(const std::list<T>& l) { print_container(l); }
template <typename T>
void print(const T& e) { cout << e; }
template <typename T>
void print_container(const T& c) {
cout << "[";
bool isFirst = true;
for (const auto& e : c) {
if (isFirst) isFirst = false;
else cout << ",";
print(e);
}
cout << "]";
}
Возможно, вам следует следовать шаблону алгоритма for_each. Перейдите по ссылке для примера того, как этот алгоритм может быть определен.
http://www.cplusplus.com/reference/algorithm/for_each/
Если вам нужен универсальный принтер, вам следует использовать итераторы в качестве входных данных для алгоритма. Тогда не имеет значения, какой контейнер их поставляет, и ваша функция может даже брать указатели на массивы C. Продолжайте в том же духе, и если вы хотите, чтобы он работал для пользовательских типов, то пользовательские типы должны иметь перегруженные потоковые операторы для их поддержки.
Взгляните на это и рассмотрите еще больше вариантов.
http://www.cplusplus.com/reference/iterator/ostream_iterator/
Я не смог опубликовать это в комментарии, но вот упрощенная версия другого ответа с основной для тестирования. Это было проверено на http://www.compileonline.com/compile_cpp11_online.php
Конечно, вам могут понадобиться специализации для других типов, таких как двойные или пользовательские типы.
#include <vector>
#include <list>
#include <iostream>
template <typename T>
void print_container(const T&);
template <typename T>
void print(const T& e) { std::cout << e; }
template <typename T>
void print_container(const T& c) {
std::cout << "[";
bool isFirst = true;
for (const auto& e : c) {
if (isFirst) isFirst = false;
else std::cout << ",";
print(e);
}
std::cout << "]";
}
int main()
{
std::vector<int> v = {0, 1, 3, 5, 7, 9};
std::list<int> l = {0, 1, 3, 5, 7, 9};
print_container(v);
print_container(l);
}