Матричный шаблон C ++, неоднозначность между умножением матрицы на матрицу и числом матриц

Я пишу матричный шаблон класса. Все шло хорошо, пока я не перегрузил оператор умножения. Мой класс выглядит так:

template <typename TNum> class Matrix
{
private:
// ...
TNum* Data;
public:
const TMatIdx NRows; // Type TMatIdx defined somewhere else.
const TMatIdx NCols;
const TMatIdx Size;

// ...
// Matrix * matrix
template <typename T2>
const Matrix<TNum> operator*(const Matrix<T2>& right) const;

// Matrix * number
template <typename T2>
Matrix<TNum>& operator*=(const T2& scale);
};

// Matrix * number
template <typename TNum, typename T2>
Matrix<TNum> operator*(Matrix<TNum> lhs, const T2& rhs);
// Number * matrix
template <typename TNum, typename T2>
Matrix<TNum> operator*(const T2& lhs, Matrix<TNum> rhs);

Я надеюсь охватить все возможные комбинации умножения матриц и чисел одинаковыми * оператор.

Затем я написал небольшую тестовую программу, которая умножает два Matrix<double>s, мой компилятор clang ++ жалуется на неоднозначность:

test.cpp:46: error: ambiguous overload for 'operator*' in 'M * N'
matrix.h:225: note: candidates are: const QCD::Matrix<TNum> QCD::Matrix<TNum>::operator*(const QCD::Matrix<T2>&) const [with T2 = double, TNum = double]
matrix.h:118: note:                 QCD::Matrix<TNum> QCD::operator*(const T2&, QCD::Matrix<TNum>) [with TNum = double, T2 = QCD::Matrix<double>]
matrix.h:109: note:                 QCD::Matrix<TNum> QCD::operator*(QCD::Matrix<TNum>, const T2&) [with TNum = double, T2 = QCD::Matrix<double>]
test.cpp:52: error: ambiguous overload for 'operator*' in 'M * N'
matrix.h:225: note: candidates are: const QCD::Matrix<TNum> QCD::Matrix<TNum>::operator*(const QCD::Matrix<T2>&) const [with T2 = double, TNum = double]
matrix.h:118: note:                 QCD::Matrix<TNum> QCD::operator*(const T2&, QCD::Matrix<TNum>) [with TNum = double, T2 = QCD::Matrix<double>]
matrix.h:109: note:                 QCD::Matrix<TNum> QCD::operator*(QCD::Matrix<TNum>, const T2&) [with TNum = double, T2 = QCD::Matrix<double>]

Можно ли преодолеть эту неоднозначность без необходимости явно записывать все возможные специализации для T2?

И к вашему сведению, вот моя реализация:

template<typename TNum> template <typename T2>
Matrix<TNum>& Matrix<TNum> ::
operator*=(const T2& rhs)
{
for(TMatIdx i = 0; i < Size; i++)
Data[i] *= rhs;
return *this;
}

template<typename TNum> template <typename T2>
const Matrix<TNum> Matrix<TNum> ::
operator*(const Matrix<T2>& right) const
{
Matrix<TNum> c(NRows, right.NCols);
TNum sum_elems;
for(TMatIdx i = 0; i < NRows; i++)
{
for(TMatIdx j = 0; j < right.NCols; j++)
{
sum_elems = TNum(0);
for(TMatIdx k = 0; k < right.NRows; k++)
{
sum_elems += at(i, k) * right.at(k, j);
}

c.at(i, j) = sum_elems;
}
}
return c;
}template <typename TNum, typename T2>
Matrix<TNum> operator*(Matrix<TNum> lhs, const T2& rhs)
{
lhs *= rhs;
return lhs;
}

template <typename TNum, typename T2>
Matrix<TNum> operator*(const T2& lhs, Matrix<TNum> rhs)
{
rhs *= lhs;
return rhs;
}

0

Решение

Я собираюсь описать решение с помощью C ++ 11, а затем объяснить, как реализовать его в C ++ 98.

В с ++ 11 заголовок <type_traits> включает функции типа и предикаты типа. Это делает соблюдение ограничений более удобным.

std::is_same<T1, T2>::value верно, если T1 тот же тип, что и T2 и ложь в противном случае.

typename std::enable_if< bool, T >::type это четко определенный тип T если bool верно и плохо определено иначе.

Когда компилятор ищет подходящие шаблонные функции и методы, он не выдает ошибку, если попытка специализации не удалась. Это просто выбрасывает этого кандидата. Это означает, что следующий код удалит неоднозначность:

template <typename TNum, typename T2>
typename std::enable_if< (!std::is_same<Matrix<TNum>, T2>::value),
Matrix<TNum> >::type operator*(const T2& lhs, Matrix<TNum> rhs);

При принятии решения принимаются во внимание только декларации. Вышеприведенная логика семантически разумна, но на глазок читать. Таким образом, c ++ 11 поддерживает псевдонимы шаблонов и функции constexpr.

template<bool B, typename T = void>
using Enable_if = typename std::enable_if<B, T>::type;

template<typename T1, typename T2>
constexpr bool Is_same(){
return std::is_same<T1, T2>::value;
}

Вышеуказанное становится:

template <typename TNum, typename T2>
Enable_if<( !Is_same<Matrix<TNum>, T2>() ),
Matrix<TNum> > operator*(const T2& lhs, Matrix<TNum> rhs);

Концепции предоставят инструменты, чтобы сделать это более удобным.

Теперь, если у вас нет C ++ 11, вы не получите конфетку. Но Boost предоставляет те же функции. Предположим, что у вас их тоже нет, их реализация не страшна.

Функции времени компиляции зависят от нескольких языковых правил, что затрудняет их понимание. Мы рассмотрим enable_if первый. Мы хотим typename enable_if<true, T>::type быть четко определенным, но, typename enable_if<false, T>::type быть ерундой Мы используем специализацию:

template<bool B, typename T = void>
struct enable_if {
typedef T type;
};

template<typename T>
struct enable_if<false, T> {};

Уведомление ложно обрабатывает ровно половину соответствующих дел. Это стоит пережевать выше.

Для того, чтобы реализовать is_sameнам нужно понятие истинного или ложного во время компиляции. Мы можем гарантировать это с помощью статических константных переменных. Мы хотим is_same иметь истинное значение времени компиляции, когда аргументы шаблона совпадают. Правила системы специализации обрабатывают это напрямую.

template<typename, typename>
struct is_same{
static const bool value = false;
};

template<typename T>
struct is_same<T, T>{
static const bool value = true;
};

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

struct false_type {
static const bool value = false;
};

struct true_type {
static const bool value = true;
};

затем is_same будет выглядеть так:

template<typename, typename>
struct is_same : false_type {};

template<typename T>
struct is_same<T, T> : true_type {};

что делает его больше похожим на функцию.

Я предпочитаю это решению по категориям, потому что проще разделить метапрограмму в заголовочный файл. Затем вы можете повторно использовать логику в другом месте. Тем не менее, если вы не используете c ++ 11 или даже boost, создание необходимых функций времени компиляции может быть головной болью.

Если использование комплекса (или любой простой редизайн) удовлетворяет ваши текущие и будущие требования — предпочтите это. В противном случае я думаю, что это решение вполне обосновано на будущее.

3

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

Это может помочь.

#include<utility>

template<class Type>
struct test{
private:
struct _matrix{};
struct _scalar{};
template<class T>
struct category{
typedef _scalar type;
};
template<class T>
struct category<test<T>>{
typedef _matrix type;
};

template<class T>
void do_foo(T, _matrix){}

template<class T>
void do_foo(T, _scalar){}

public:
//c++11
template<class T>
void operator*(T&& a){
do_foo(std::forward<T>(a), category<T>::type());
}
//Older Compiler
//template<class T>
//void operator*(const T& a){
//  do_foo(a, category<T>::type());
//}

};

int main(){
typedef test<int> int_matrix;
int_matrix obj;
obj*int_matrix();
obj*obj;
obj*1;
obj*1.;

return 0;
}
0

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector