[C++] Выбор функции-шаблона

Sanjaz

Добрый день. Помогите пожалуйста решить такую проблемку.
Вот код:

// тип-метка для матриц
struct matrix_tag {};

// класс матриц с элементами типа T
template <class T>
class matrix
{
public:
typedef matrix_tag tag;
};

// результат отложенной операции перемножения матриц M1 и M2
template <class M1, class M2>
struct result1
{
result1(const M1& m1, const M2& m2)
{
}
};

// результат отложенной операции умножения матрицы M на скаляр T
template <class T, class M>
struct result2
{
result2(const T& t, const M& m)
{
}
};

// сами операции
template <class M1, class M2>
result1<M1, M2> operator*(const M1& m1, const M2& m2)
{
// Надо потребовать от типов M1 и M2 чтобы в них был определен тип tag. Как это сделать?
return result1<M1, M2>(m1, m2);
}

template <typename T, class M>
result2<T, M> operator*(const T& t, const M& m)
{
// Надо потребовать от типа M чтобы в нем был определен тип tag. Как это сделать?
return result2<T, M>(t, m);
}

int main(int argc, char* argv[])
{
matrix<double> m;

5 * m; // Здесь компилятор ругается: 'operator *' is ambiguous. И правильно делает.

return 0;
}

Как потребовать от параметра шаблона M, чтобы в нем был определен некоторый тип tag?

slonishka

может можно все матрицы отнаследовать от базового класса и ссылку на него передавать, как параметр в operator*?
тогда у шаблонного operator* будет только один параметр <typename T> и специализация для базового класса матриц.
тогда и matrix_tag тебе не нужен будет.

Sanjaz

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

slonishka

ну ты еще и инлайниться захотел при такой универсальности!
там с наследованием тоже свои сложности (например, operator* должен возвращать по значению, то есть придется кастить MatrixBase в один из наследуемых типов, создавать экземпляр этого типа и возвращать его значение). кроме того, я что-то пока не вкурил, можно ли шаблонную функцию сделать виртуальной (я сейчас пытался то же самое через operator*= реализовать).
а что тебе нужно? уметь перемножать матрицы с различными типами контента и уметь умножать матрицу на любой численный тип?

Sanjaz

ну ты еще и инлайниться захотел при такой универсальности!
Ну вообще да .
Перемножать я могу матрицы только с одинаковым типом содержимого (имеется соответствующая проверка). Кроме того я матрицы не кастю. Просто у "конечной" матрицы я определяю коструктор типа такого:

template <class T>
class matrix
{
...
public:
template <class Mx>
matrix(const Mx& m)
{
...
}
};

Таким образом я инициализирую матрицы из любой другой матрицы

Sanjaz

Мне нужно чтобы компилялся код в первом посте

slonishka

Перемножать я могу матрицы только с одинаковым типом содержимого (имеется соответствующая проверка).
тогда зачем тебе матрицы, как параметры шаблона?
можно же сделать две шаблонные функции:
template<typename T> matrix<T> operator* (T& lhs, matrix<T>& rhs);
template<typename T> matrix<T> operator* (matrix<T>& lhs, matrix<T>& rhs);

slonishka

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

Sanjaz

Проблема в том, что у меня кроме типа matrix есть еще куча других типов матриц. Например, результаты result1 и result2 у меня также являются матрицами. И эти типы никак не связянны между собой кроме того, что у них должны быть определенны matrix_tag, value_type, value_type operatorsize_t row, size_t col size_t rows size_t columns. Поэтому я не могу написать так:

template<typename T> matrix<T> operator* (T& lhs, matrix<T>& rhs);
template<typename T> matrix<T> operator* (matrix<T>& lhs, matrix<T>& rhs);

slonishka

Проблема в том, что у меня кроме типа matrix есть еще куча других типов матриц. Например, результаты result1 и result2 у меня также являются матрицами.
что такое тип матрицы?
матрица — это же такая абстракция, которая параметризуется типом ее элемента (и размерностью).
проблемы с размерностью никто не мешает устранить прямо в operator* (все равно новую матрицу создавать придется
типы тебя не интересуют. чем же еще могут отличаться два типа матриц кроме размерности и типа элемента?

Sanjaz

Кроме matrix<T> у меня есть diag_matrix<T>, mult_matrix<T>, scalar_mult_matrix<T> и много других еще типов. Если я для каждой пары типов буду определять операции, то я просто подохну

slonishka

это для оптимизации?
имхо, не очень хорошо определять одинаковые сущности в независимых классах.
а если тебе надо будет заполнить матрицу произвольными значениями, как ты узнаешь, экземпляр какого класса создавать: matrix<T> или diag_matrix<T>. а если изначально предположить, что она diag_, то в случае ошибки оператор преобразования типа чтоли определять?
имхо, это жесть. лучше уж определить в matrix<T> биты свойств. ну или отнаследовать все эти типы (включая произвольную матрицу) от базового matrix<T>.

slonishka

ну то есть если ты уверен, что так делают в ленивых библиотеках матричных вычислений, то делай.
но я что-то в этом очень сомневаюсь, честно говоря.

Sanjaz

это для оптимизации?
да
а если тебе надо будет заполнить матрицу произвольными значениями, как ты узнаешь, экземпляр какого класса создавать: matrix<T> или diag_matrix<T>
Это всегда знает пользователь

buka

Поиск перегруженной функции совершается по ее описанию, в теле функции же что-то требовать бессмысленно. С тегами было бы красиво, но, думается, нельзя без оборачивания базовых типов.
Как вариант — ввести описательный класс:
// класс матричных характеристик
template <class T>
struct matrix_traits
{
static const bool is_matrix = false;
};

// класс матриц с элементами типа T
template <class T>
class matrix
{
};

// характеристики matrix
template <class T>
struct matrix_traits<matrix<T> >
{
static const bool is_matrix = true;
};

// результат отложенной операции перемножения
template <class M1, class M2, bool NonscalarM1 > struct result;

// результат отложенной операции перемножения матриц M1 и M2
template <class M1, class M2>
struct result<M1, M2, true>
{
result(const M1& m1, const M2& m2)
{
}
};

// результат отложенной операции умножения матрицы M на скаляр T
template <class T, class M>
struct result<T, M, false>
{
result(const T& t, const M& m)
{
}
};


// сами операции
template <class M1, class M2>
result<M1, M2, matrix_traits<M1>::is_matrix> operator*(const M1& m1, const M2& m2)
{
return result<M1, M2, matrix_traits<M1>::is_matrix>(m1, m2);
}

int main(int argc, char* argv[])
{
matrix<double> m;

5 * m;

return 0;
}
Оставить комментарий
Имя или ник:
Комментарий: