Я пытаюсь создать свой первый шаблон, используя этот удивительный гид, но я сталкиваюсь с проблемой, для которой я не знаю, как обрабатывать несколько вложенных аргументов:
Исходная функция, которую я создал, была реализована следующим образом:
QVector< QVector<double> > transpose(QVector< QVector<double> > &matrix)
{
int pre_numcols = matrix.size();
int pre_numrows = matrix[0].size();
QVector< QVector<double> > transposed(pre_numrows);
QVector<double> newcols(pre_numcols);
qFill(newcols.begin(), newcols.end(), 0.0);
qFill(transposed.begin(), transposed.end(), newcols);
qDebug()<<transposed.size();
qDebug()<<transposed[0].size();
for (int i = 0; i < pre_numcols; ++i)
{
for (int j = 0; j < pre_numrows; ++j)
{
transposed[j][i] = matrix[i][j];
}
}
return transposed;
}
Учитывая, что я только перетасовываю пункты, многие типы будут возможны.
QVector
может быть заменен std::vector
, double
может быть заменен даже string
,
Я получил это далеко:
template<class T>
T transpose(T &matrix)
{
int pre_numcols = matrix.size();
int pre_numrows = matrix[0].size();
T transposed(pre_numrows);
QVector<double> newcols(pre_numcols); // How to do this one?
Если я читаю T
как «контейнер, вмещающий контейнер«Как я могу объявить newcols
переменная, учитывая, что это подмножество?
Примечание: я буду редактировать qFill()
часть, чтобы удовлетворить другие случаи, такие как std::
позже.
Вы можете использовать шаблоны в качестве параметра шаблона:
template <
template <typename> class Container,
typename ValueType
>
void foo(Container<ValueType> const & c);
Таким образом, ваш шаблон станет:
template <
template <typename> class Container,
typename ValueType
>
Container< Container<ValueType> > transpose(Container< Container<ValueType> > & matrix)
{
int pre_numcols = matrix.size();
int pre_numrows = matrix[0].size();
Container< Container<ValueType> > transposed(pre_numrows);
Container<ValueType> newcols(pre_numcols);
// ...
return transposed;
}
Это было бы здорово, если бы это работало так. Но, как всегда в реальности, возникают новые проблемы! std::vector
не является template <typename T> class vector
но
template <
typename T,
typename Allocator = allocator<T>
>
class vector
Таким образом, мы должны изменить нашу функцию
template <
template <typename, typename> class C,
typename T,
template <typename> class A = std::allocator,
typename InnerType = C< T, A<T> >,
typename OuterType = C< InnerType, A<InnerType> >
>
OuterType transpose(OuterType & matrix)
{
int pre_numcols = matrix.size();
int pre_numrows = matrix[0].size();
OuterType transposed(pre_numrows);
InnerType newcols(pre_numcols);
// ...
return transposed;
}
что гораздо менее изящно, и я не уверен, что контейнеры Qt совместимы с этим.
Поскольку вы сказали, что C ++ 11 можно использовать для вас, вы можете перейти с первой функцией (используя template <typename> class Container
) и использование шаблонных псевдонимов для стандартных контейнеров:
template <typename T> using vector = std::vector<T, std::allocator<T>>;
vector<vector<int>> v;
auto vprime = transpose<vector>(v);
Это еще одно возможное решение, которое использует метафункцию вспомогательного шаблона, и я думаю, что она чище, чем предыдущие:
namespace
{
template <typename T> struct get_inner_i {};
template <template <typename> class T, typename Inner>
struct get_inner_i<T<Inner>> { typedef Inner type; };
template <
template <typename, typename> class T,
typename Inner,
template <typename> class Allocator
> struct get_inner_i<T<Inner, Allocator<Inner>>> { typedef Inner type; };
template <typename T> using get_inner = typename get_inner_i<T>::type;
}
template <typename MatrixType>
MatrixType transpose(MatrixType const & matrix)
{
auto const nrows = matrix.size();
auto const ncols = nrows > 0 ? matrix[0].size() : 0;
MatrixType transposed(ncols, get_inner<MatrixType>(nrows));
for(auto k = 0; k < nrows; ++k)
for(auto j = 0; j < ncols; ++j)
transposed[j][k] = matrix[k][j];
return transposed;
}
Почему это гораздо более подробное решение лучше? Дело не в том, сколько кода нам нужно написать, а в том, насколько проста и интуитивно понятна наша функция для пользователя. В этой версии мы снова имеем только T
в качестве аргумента шаблона, поэтому явное указание аргумента шаблона для функции необязательно.
Кроме того, наша мета-функция может определить, является ли она контейнером с пользовательским параметром распределителя, или нет, и пользователю не нужно выполнять using
трюк с псевдонимом шаблона, упомянутый выше.
Это еще одно решение, снова превосходящее предыдущие, которое использует std::begin
определить тип значения контейнера безотносительно к параметрам его шаблона, или является ли он шаблоном, если он обеспечивает begin
а также end
итераторы, или массив в стиле C:
namespace
{
template <typename Container>
struct value_type_i
{
typedef typename std::decay<
decltype(*std::begin(std::declval<
Container const &
>()))
>::type type;
};
template <typename Container>
using value_type = typename value_type_i<Container>::type;
}
template <typename MatrixType>
MatrixType transpose(MatrixType const & matrix)
{
auto const nrows = matrix.size();
auto const ncols = nrows > 0 ? matrix[0].size() : 0;
MatrixType transposed(ncols, value_type<MatrixType>(nrows));
for(auto k = 0; k < nrows; ++k)
for(auto j = 0; j < ncols; ++j)
transposed[j][k] = matrix[k][j];
return transposed;
}
Вы можете увидеть это работает здесь: http://ideone.com/cCAyFD
Других решений пока нет …