Avoid highly visible unconstrained templates with common names
Reason
An unconstrained template argument is a perfect match for anything so such a template can be preferred over more specific types that require minor conversions. This is particularly annoying/dangerous when ADL is used. Common names make this problem more likely.
Example
namespace Bad {
struct S { int m; };
template<typename T1, typename T2>
bool operator==(T1, T2) { cout << "Bad\n"; return true; }
}
namespace T0 {
bool operator==(int, Bad::S) { cout << "T0\n"; return true; } // compare to int
void test()
{
Bad::S bad{ 1 };
vector<int> v(10);
bool b = 1 == bad;
bool b2 = v.size() == bad;
}
}
This prints T0 and Bad.
Now the == in Bad was designed to cause trouble, but would you have spotted the problem in real code? The problem is that v.size() returns an unsigned integer so that a conversion is needed to call the local ==; the == in Bad requires no conversions. Realistic types, such as the standard-library iterators can be made to exhibit similar anti-social tendencies.
Note
If an unconstrained template is defined in the same namespace as a type, that unconstrained template can be found by ADL (as happened in the example). That is, it is highly visible.
Note
This rule should not be necessary, but the committee cannot agree to exclude unconstrained templates from ADL.
Unfortunately this will get many false positives; the standard library violates this widely, by putting many unconstrained templates and types into the single namespace std.
Enforcement
Flag templates defined in a namespace where concrete types are also defined (maybe not feasible until we have concepts).