I am a couple of years late, but I wanted to offer a contrasting view on this topic from the other answers given. Consider the case of designing a traits class for various ABIs supported by an implementation. For instance, consider the case of designing a traits class for simd ABIs:
template<typename T, typename Abi>
struct abi_traits {};
template<>
struct abi_traits<float, sse>
{
using vector_type = __m128;
[[nodiscard]] static constexpr auto operator&(auto a, auto b) noexcept
{
return _mm_and_ps(a, b);
}
};
You'd then be able to call operator overloads for each abi:
abi_traits<T, Abi>::operator&(a, b);
This may seem contrived, but consider the alternative where a named function is used as opposed to an operator overload:
abi_traits<T, Abi>::and(a, b);
You might recognize that "and" is a keyword in C++, and this therefore does not compile. You'd have to mangle the name in some way/shape/form to make this work:
abi_traits<T, Abi>::op_and(a, b);
So then from your vector class, you would have to provide the implementation like this:
template<typename T, typename Abi>
struct simd
{
...
constexpr auto operator&(auto b) noexcept
{
traits<T, Abi>::op_and(this->value, b);
}
};
And you certainly could do this, but then you have to maintain and understand the mangled-name syntax imposed by the programmer. It is much more clear in my mind to allow static overloads, as this would better show intent in the code:
template<typename T, typename Abi>
struct simd
{
...
constexpr auto operator&(auto b) noexcept
{
traits<T, Abi>::operator&(this->value, b);
}
};
So... I get why the other answers might be wary of this... but I totally think there is merit to having static operator overloads where possible. I have seen some proposals appear in the past regarding various efforts to add static operator overloads (for instance, this proposal for static operator()). Perhaps this merits a larger discussion or even a proprosal. I certainly have run into many situations where I thought to myself that it would be much clearer to have this feature. Naming is hard, and it's an underrated problem. If I saw a function with the form op_shl(auto a, auto b), I would be less certain what to do with this function than if I saw a function of the form: operator<<(auto a, auto b). And we can make all the arguments we want about writing good comments and following best-practices with software designs, but at the end of the day, there is no ambiguity with the static operator syntax.