The type equations aren't all "X = T", though. Consider inference on casts, such as `{1, 2, 3}` to `std::vector`:
#include<vector>
float make(float) { return 0; }
int make(std::vector<int>) { return 0; }
int main(int, char **) {
auto x = {1, 2, 3};
auto y = make(x);
}
You've also got return type deduction and overloading,
template <typename T>
auto id(T val) { return val; }
int main(int, char **) {
auto x = 7;
auto y = id(x);
}
and even stupid template tricks
template <typename T, typename U,
typename = std::enable_if_t<std::is_same<T, U>::value>>
auto add(T lhs, U rhs) { return lhs + rhs; }
template <typename T, typename U,
typename = std::enable_if_t<!std::is_same<T, U>::value>>
float add(T lhs, U rhs) { return lhs + rhs; }
int main(int, char **) {
auto x = 7;
auto y = add(x, x);
}
Whilst all the type inference is still one-directional and falls out template expansion, it's still legitimate inference.