15 September 2023

C++ 核心指南目录

“Avoid highly visible unconstrained templates with common names”

理由

模板的未限定的参数可以匹配任何东西,所以会比特定类型的参数的模板更容易匹配到。在用了 ADL 的时候,会很讨厌、很危险。常见名字使得问题更糟糕。

例子

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;          // T0
        bool b2 = v.size() == bad;  // Bad
    }
}
int main()
{
    T0::test();
}
T0
Bad

这里, Bad 中的 == 引起了问题。但是你能在真实代码中发现这个问题吗?问题是这样的, v.size() 返回一个无符号整型,所以如果要用 T0 中的 == 操作符,需要一次类型转换。而 Bad 中的 == 不需要类型转换。像标准库中的迭代器等真实类型就有这种反社会倾向。

注意

如果你在同一个名字空间中定义一个未限定的模板,ADL 会发现这个未定义的模板(正如例子中所发生的)。因为这个模板的可见度更高。

注意

本来不应该有这条规则,但是 C++ 标准委员会成员不同意把未定义的模板排除在 ADL 之外。

很遗憾,这会导致很多误报。而且标准库也很大程度上违反此规则,把很多未限定的模板和类型放在单独的 std 名字空间。

强化

标记在同一个名字空间里有具体类型又有未限定模板的情况(可能直到我们有 concepts 之后才可以做到)