2014-11-26 2 views
2

Я пытаюсь лучше понять шаблоны и обратился к хорошему классу «ole matrix». Я знаю о себе, броненосец и т. Д. Моя цель - лучше понять шаблоны. Мой вопрос в том, как вы получаете функцию-член для принятия аргумента, который является объектом одного и того же класса шаблонов, но с другой специализацией?Параметр функций-членов класса шаблона

Например, класс матрицы, который я пытаюсь собрать, содержит два параметра шаблона - количество строк и количество столбцов. Кроме того, любая тхп матрица объекта (Matrix<mRows,nCols>) должен быть в состоянии принять NXP матричный объект (Matrix<nCols,pCols>) и умножить их вместе и вернуть MXP матричный объект (Matrix<mRows,pCols>):

template <unsigned mRows, unsigned nCols> 
class Matrix 
{ 
private: 
    double matrixData[mRows][nCols]; 
//...other stuff 
public: 
//...other stuff 

    Matrix operator * (const Matrix<nCols, pCols>& rhs); 
} 

// simple naive matrix multiplication method 
template <unsigned mRows, unsigned nCols> 
Matrix<nCols,pCols> Matrix<mRows, nCols>::operator * (const Matrix<nCols,pCols>& rhs) 
{ 
    Matrix<nCols,pCols> temp(); 

    for (int r = 0; r<mRows; ++r) 
    { 
     for(int c = 0;c<pCols;++c) 
     { 
      temp.matrixData[r][c]=0; 
      for (int elem = 0; elem<nCols;++elem) 
      { 
       temp.matrixData[r][c]+= matrixData[r][elem]*rhs.matrixData[elem][c]; 
      } 
     } 
    } 

    return temp; 
} 

Основная функция будет что-то вроде:

int main() 
{ 
    Matrix<2,3> m1; 
    Matrix<3,4> m2; 

    //...initialize matrices... 

    Matrix<2,4> m3 = m1 * m2; 

} 

Это не работает, потому что pCols не объявлен нигде. Где/как это должно быть объявлено?

+0

Я понимаю, что цель этого класса - лучшее понимание шаблонов. Но в любом случае вы всегда должны помнить, что компилятор генерирует код для каждого параметра шаблона (в отличие от java generics). Это означает, что если вы будете использовать этот класс для умножения матриц ([N, M] * [M, K]) с разными размерами, тогда количество сгенерированного кода будет также зависеть от количества уникальных триплетов размеров ([N, M, K]) – antonpp

ответ

0

Итак, после того, как я некоторое время возился с этим, я наконец получил решение, используя предложения от Columbo. Первое решение сохраняет оператор умножения в функции члена, а затем делает все специализации друзья друг с другом, так что они могут изменить друг друг личные данные:

template <unsigned mRows, unsigned nCols> 
class Matrix 
{ 
private: 
    double matrixData[mRows][nCols]; 

public: 

    template<unsigned nRows, unsigned pCols> // make all specializations of the templates friends with each other 
    friend class Matrix; 

    // ... constructor and other operator definitions/prototypes here 

    // define proper matrix multiplication 
    // should be defined such that Matrix<mRows,pCols> = Matrix<mRows,nCols>*Matrix<nCols*pCols> 
    // since the inner dimensions of the matrix must be the same. 
    template <unsigned pCols> 
    Matrix<mRows,pCols> operator * (const Matrix<nCols, pCols>& rhs) const; 
}; 

template <unsigned mRows, unsigned nCols> 
template <unsigned pCols> 
Matrix<mRows,pCols> Matrix<mRows, nCols>::operator * (const Matrix<nCols,pCols>& rhs) const 
{ 
    Matrix<mRows,pCols> temp; 

    for (unsigned r = 0; r<mRows; ++r) 
    { 
     for(unsigned c = 0;c<pCols;++c) 
     { 
      temp.matrixData[r][c]=0; 
      for (unsigned elem = 0; elem<nCols;++elem) 
      { 
       temp.matrixData[r][c]+= matrixData[r][elem]*rhs.matrixData[elem][c]; 
      } 
     } 
    } 

    return temp; 
} 

вторым решением следует предложению Columbo, чтобы сделать оператор умножения friend не -member:

template <unsigned mRows, unsigned nCols> 
class Matrix 
{ 
private: 
    double matrixData[mRows][nCols]; 

public: 

    // ... constructor and other operator definitions/prototypes here 

    // define proper matrix multiplication 
    // should be defined such that Matrix<mRows,pCols> = Matrix<mRows,nCols>*Matrix<nCols*pCols> 
    // since the inner dimensions of the matrix must be the same. 

    template <unsigned m, unsigned n, unsigned p> 
    friend Matrix<m,p> operator * (const Matrix<m,n>& lhs, const Matrix<n, p>& rhs); 
}; 

template <unsigned m, unsigned n, unsigned p> 
Matrix<m,p> operator * (const Matrix<m,n>& lhs, const Matrix<n, p>& rhs) 
{ 
    Matrix<m,p> temp; 

    for (unsigned r = 0; r<m; ++r) 
    { 
     for(unsigned c = 0;c<p;++c) 
     { 
      temp.matrixData[r][c]=0; 
      for (unsigned elem = 0; elem<n;++elem) 
      { 
       temp.matrixData[r][c]+= lhs.matrixData[r][elem]*rhs.matrixData[elem][c]; 
      } 
     } 
    } 

    return temp; 
} 

Если кто-нибудь может прокомментировать, почему один лучше другого, было бы здорово.Я думаю, что второе решение лучше, поскольку именно эта функция специализирована для разных комбинаций, а не для всего класса (правильно?). Например, в Matrix<3,3> * Matrix<3,4> vs Matrix<3,3> * Matrix<3,5> класс Matrix<3,3> должен был бы только специализироваться один раз, а * operator был бы специализированным для покрытия обоих случаев. Правильно?

0

Функция operator* нуждается только в одном параметре шаблона. Количество строк RHS должно быть таким же, как количество столбцов LHS.

template <unsigned pCols> 
Matrix<nRows, pCols> operator * (const Matrix<nCols, pCols>& rhs) 
{ 
    //... 
} 
1

operator* Пользовательский шаблон функции сам. То есть написать внутри шаблона класса

template <unsigned pCols> 
Matrix operator * (const Matrix<nCols, pCols>& rhs); 

и за ее пределами использование двух списков параметров:

template <unsigned mRows, unsigned nCols> 
template <unsigned pCols> 
Matrix<mRows, pCols> Matrix<mRows, nCols>::operator * (const Matrix<nCols,pCols>& rhs) 

Однако, я рекомендую вам использовать функцию в friend, не являющемуся членом.

+0

Я думаю, что это правильная идея - внутри шаблона класса мне пришлось изменить прототип внутри класса на 'template Matrix operator * (const Matrix & rhs);' , однако мне не удается получить доступ к закрытым членам класса. Может ли одна специализация шаблона не обращаться к закрытым членам другой специализации того же шаблона? –

+0

@ Джейсон Нет, почему бы так быть «Это разные классы. – Columbo

+0

Если вы умножаете две« матрицы »<3,3>' 'вместе, они могут без проблем получить доступ к частным членам друг друга. Однако с помощью« Матрицы»'times a' Matrix <3,4> 'По-видимому, вы не можете этого сделать, но было бы неплохо, если бы вы могли –

1

Вы должны использовать аргументы шаблона, доступные в вашем определении шаблона функции специализироваться класса Matrix, в вашем случае:

template <unsigned mRows, unsigned nCols> 
Matrix<mRows,nCols> Matrix<mRows, nCols>::operator * (const Matrix<nCols,mRows>& rhs) 

Тогда вы снова лучше использовать последовательное соглашение об именах для аргументов шаблона в как декларация, так и определение.

Подумайте о аргументах шаблона как о типах/константах, доступных для использования в сущности, которая следует за ним. Объявления - это определения, по сути, являются отдельными объектами в этом контексте (поэтому вам необходимо ввести шаблон <> во второй раз при предоставлении определения функции).

Редактировать: После того, как вы внимательно прочитали вопрос, выясняется, что мой ответ пропускает точку. Ответ Колумба - это путь.

Смежные вопросы