When defining a member function of a class template outside the class definition, the rules for template parameter repetition depend on the context:
:: (Qualified Name)You must repeat the template parameters because you're specifying the full type of the class template:
template<std::signed_integral T>
Ratio<T>& Ratio<T>::operator+=(...) { ... }
// ^^^^^^^^ Required: `Ratio<T>`
You must repeat the template parameters because the return type is outside the scope of the class:
template<std::signed_integral T>
Ratio<T>& Ratio<T>::operator+=(...) { ... }
// ^^^^^^^ Required: `Ratio<T>&`
You do not need to repeat them because:
The Ratio<T>:: prefix already establishes the scope.
The compiler can deduce Ratio inside the parameter list as Ratio<T> (due to injected-class-name behavior).
template<std::signed_integral T>
Ratio<T>& Ratio<T>::operator+=(const Ratio& R) { ... }
// ^^^^^^ No `<T>` needed here
And why this happen ?
Ratio<T>:template<std::signed_integral T>
struct Ratio {
Ratio& operator+=(const Ratio& R); // `Ratio` = `Ratio<T>`
};
:: and in the return type. However, after ::, the compiler knows Ratio refers to Ratio<T>.