#include <iostream>
using namespace std;
namespace X
{
template<typename T> struct Mat { typedef T value_type; };
template<typename T> struct MatExpr {};
template<typename T>
MatExpr<T> prod(Mat<T> const& A, Mat<T> const& B) { return MatExpr<T>(); }
}
struct Mat2 {};
template<typename T>
X::Mat<T> prod(const X::Mat<T> &A, const Mat2 &B) { return X::Mat<T>(); }
template<typename T1, typename T2>
auto operator *(const T1 &a, const T2 &b) -> decltype(X::prod(a, b))
{
cout << __PRETTY_FUNCTION__ << '\n';
return X::prod(a, b);
}
template<typename T1, typename T2>
auto operator *(const T1 &a, const T2 &b) -> decltype(::prod<typename T1::value_type>(a, b))
{
cout << __PRETTY_FUNCTION__ << '\n';
return ::prod(a, b);
}
int main()
{
X::Mat<int> a, b;
a * b;
Mat2 c;
a * c;
}
I2luY2x1ZGUgPGlvc3RyZWFtPgp1c2luZyBuYW1lc3BhY2Ugc3RkOwoKbmFtZXNwYWNlIFgKewogICAgdGVtcGxhdGU8dHlwZW5hbWUgVD4gc3RydWN0IE1hdCB7IHR5cGVkZWYgVCB2YWx1ZV90eXBlOyB9OwogICAgdGVtcGxhdGU8dHlwZW5hbWUgVD4gc3RydWN0IE1hdEV4cHIge307CgogICAgdGVtcGxhdGU8dHlwZW5hbWUgVD4KICAgIE1hdEV4cHI8VD4gcHJvZChNYXQ8VD4gY29uc3QmIEEsIE1hdDxUPiBjb25zdCYgQikgeyByZXR1cm4gTWF0RXhwcjxUPigpOyB9Cn0KCnN0cnVjdCBNYXQyIHt9OwoKdGVtcGxhdGU8dHlwZW5hbWUgVD4KWDo6TWF0PFQ+IHByb2QoY29uc3QgWDo6TWF0PFQ+ICZBLCBjb25zdCBNYXQyICZCKSB7IHJldHVybiBYOjpNYXQ8VD4oKTsgfQoKCnRlbXBsYXRlPHR5cGVuYW1lIFQxLCB0eXBlbmFtZSBUMj4KYXV0byBvcGVyYXRvciAqKGNvbnN0IFQxICZhLCBjb25zdCBUMiAmYikgLT4gZGVjbHR5cGUoWDo6cHJvZChhLCBiKSkKewoJY291dCA8PCBfX1BSRVRUWV9GVU5DVElPTl9fIDw8ICdcbic7CiAgICByZXR1cm4gWDo6cHJvZChhLCBiKTsKfQoKdGVtcGxhdGU8dHlwZW5hbWUgVDEsIHR5cGVuYW1lIFQyPgphdXRvIG9wZXJhdG9yICooY29uc3QgVDEgJmEsIGNvbnN0IFQyICZiKSAtPiBkZWNsdHlwZSg6OnByb2Q8dHlwZW5hbWUgVDE6OnZhbHVlX3R5cGU+KGEsIGIpKQp7Cgljb3V0IDw8IF9fUFJFVFRZX0ZVTkNUSU9OX18gPDwgJ1xuJzsKICAgIHJldHVybiA6OnByb2QoYSwgYik7Cn0KCmludCBtYWluKCkKewoJWDo6TWF0PGludD4gYSwgYjsKCWEgKiBiOwoJTWF0MiBjOwoJYSAqIGM7Cn0K
decltype (X::prod(a, b)) operator*(const T1&, const T2&) [with T1 = X::Mat<int>; T2 = X::Mat<int>; decltype (X::prod(a, b)) = X::MatExpr<int>]
decltype (prod<typename T1::value_type>(a, b)) operator*(const T1&, const T2&) [with T1 = X::Mat<int>; T2 = Mat2; decltype (prod<typename T1::value_type>(a, b)) = X::Mat<int>; typename T1::value_type = int]