Создать функцию транспонирования матрицы на основе шаблона Stack Overflow

Я пытаюсь создать свой первый шаблон, используя этот удивительный гид, но я сталкиваюсь с проблемой, для которой я не знаю, как обрабатывать несколько вложенных аргументов:

Исходная функция, которую я создал, была реализована следующим образом:

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:: позже.

2

Решение

Вы можете использовать шаблоны в качестве параметра шаблона:

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

2

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]