У меня есть эта минимальная библиотека шаблонов выражений с умножением, т. Е.
template <typename T, typename U>
struct mul {
const T &v1;
const U &v2;
};
template <typename T, typename U>
mul<T, U> operator*(const T &one, const U &two) {
std::cout << " called: mul<T, U> operator*(const T &one, const T &two)\n";
return mul<T, U>{one, two};
}
и транспонированием, т. Е.
template <typename T>
struct transpose {
const T &t;
};
template <typename T>
transpose<T> tran(const T &one) {
return transpose<T>{one};
}
Я представлю некоторые типы A
и B
,где последний является подклассом первого:
template <typename T>
struct A {
T elem;
};
template <typename T>
struct B : A<T> {
B(T val) : A<T>{val} {}
};
Затем я могу вызвать мою библиотеку шаблонов выражений следующим образом (с перегрузкой для печати на std::cout
):
template <typename T, typename U>
std::ostream &operator<<(std::ostream &os, const mul<T, U> &m) {
os << " unconstrained template \n";
}
int main(int argc, char const *argv[]) {
B<double> a{2};
B<double> b{3};
std::cout << tran(a) * b << "\n";
return 0;
}
Это дает мне вывод:
called: mul<T, U> operator*(const T &one, const T &two)
unconstrained template
Пока все хорошо.Предположим теперь, что я хочу специализированный режим для «транспонирования переменной типа A<T>
, умноженной на переменную типа A<T>
для некоторого типа T
», как это было в моем main
.Для этого я введу
template <typename T>
T operator*(const transpose<A<T>> &one, const A<T> &two) {
std::cout << " called: T operator*(const A<T> &one, const A<T> &two)\n";
return one.t.elem * two.elem;
}
Я запускаю ту же функцию main
, что и выше, и все равно получаю тот же вывод, что и выше (unconstrained template
).Этого следует ожидать, поскольку transpose<B<double>>
- это совершенно другой тип по сравнению с transpose<A<double>>
, поэтому при разрешении перегрузки выбирается неограниченная версия шаблона operator*
.
(Конечно, если я изменю свои определения переменных в main
на A
вместо B
, ADL вызовет специализированную функцию и выдает called: T operator*(const A<T> &one, const A<T> &two)
и 6
).
Я недавно узнал о SFINAE, поэтому я ожидал, что следующее изменение в более конкретном операторе умножения вызовет перегрузку при перегрузке для выбора специализированной функции:
template <typename T, typename V>
std::enable_if_t<std::is_base_of<A<T>, V>::value, T> operator*(const transpose<V> &one,
const V &two) {
std::cout << " called: std::enable_if_t<std::is_base_of<A<T>, V>::value, T> operator*(const "
"transpose<V> &one, const V &two)\n";
return one.t.elem * two.elem;
}
Даже при использовании SFINAE'd operator*
Я все еще получаю unconstrained template
версию.Как так?Какие изменения я должен сделать, чтобы вызвать более специализированную функцию шаблона?