I have this minimal expression template library with a multiplication, i.e.
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};
}
and transpose, i.e.
template <typename T>
struct transpose {
const T &t;
};
template <typename T>
transpose<T> tran(const T &one) {
return transpose<T>{one};
}
I will introduce some types A
and B
, where the latter is a subclass of the former:
template <typename T>
struct A {
T elem;
};
template <typename T>
struct B : A<T> {
B(T val) : A<T>{val} {}
};
Then, I can call my expression template library as follows (with an overload for printing to 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;
}
This gives me the output :
called: mul<T, U> operator*(const T &one, const T &two)
unconstrained template
So far so good. Suppose now that I want a specialized treatment for 'transpose of a variable of type A<T>
times a variable of type A<T>
for some type T
', as I had in my main
. To this end, I will introduce
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)
and になります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'doperator*
を使用しても、バージョンを取得できunconstrained template
ます。どうして?より特化したテンプレート関数を呼び出すには、どのような変更を加える必要がありますか?