重载解决规则C++缺陷

A flaw in C++ overload resolution rules?

本文关键字:缺陷 C++ 规则 解决 重载      更新时间:2023-10-16

请考虑以下代码:

#include <iostream>
namespace ns1
{
    struct A
    {
    };
    template <class T>
    std::ostream& operator << (std::ostream& os, const T& t)
    {
        return os << "ns1::print" << std::endl;
    }
}
namespace ns2
{
    template <class T>
    std::ostream& operator << (std::ostream& os, const T& t)
    {
        return os << "ns2::print" << std::endl;
    }
    void f (const ns1::A& a)
    {
        std::cout << a;
    }
}

int main()
{
    ns1::A a;
    ns2::f (a);
    return 0;
}

编译失败,根据标准出现"不明确的过载错误"。

但是为什么?当然,A 的"home"命名空间中的"同样好"的运算符应该优先吗?有什么合乎逻辑的理由不这样做吗?

如果您希望首选

namespace A中的重载,则必须向其添加一些内容以使其实际上更好。比如说,通过使其不是模板:

namespace ns1 
{
    std::ostream& operator<<(std::ostream&, const A& );
}

否则,实际上没有概念上的理由来说明为什么一个命名空间中的函数模板比另一个命名空间中的函数模板更可取,如果两者完全相同。毕竟,为什么 A 命名空间中的函数模板会比 f 命名空间中的函数模板"更好"?f的实施者难道不会"知道得更好"吗?仅依靠函数签名可以回避此问题。

如果你仔细阅读编译器错误,歧义错误不是在ns1ns2operator<<版本之间,而是在ns1operator<<(os, const char*)实例化和namespace std完全相同的重载之间。后者被ADL拖入std::ostream

最好的方法是使用 @Barry 的建议,并在命名空间ns1中对operator<<进行反模板化,但也将与ns1::A相关的所有功能(例如 f(A) )添加到同一命名空间中:

#include <iostream>
namespace ns1
{
    struct A {};
    std::ostream& operator << (std::ostream& os, const A& t)
    {
        return os << "ns1::print" << std::endl;
    }
    void f (const A& a)
    {
        std::cout << a;
    }    
}
int main()
{
    ns1::A a;
    f(a); // rely on ADL to find ns1::operator<<(os, A)
}

现场示例

然后,命名空间ns1充当通过 ADL class A的更广泛接口。