【问题标题】:A flaw in C++ overload resolution rules?C++ 重载解析规则中的缺陷?
【发布时间】:2016-02-25 17:23:09
【问题描述】:

考虑以下代码:

#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'命名空间中的“同样好”的运算符肯定应该优先吗?有什么合乎逻辑的理由不这样做吗?

【问题讨论】:

  • 为什么你认为A的“home”命名空间中的函数应该优先于调用函数f本身的命名空间中的函数?没有办法解决这个模棱两可的问题。错误是唯一明智的做法。
  • 因为创建者比命名空间更了解应该如何打印 A?
  • 首先,它们是模板。如果创建A 的人想要确保打印A 类型的对象的特定行为,他们将提供重载或特化。这将解决这里的歧义。其次,命名空间可以多次打开和关闭,所以A的实现者可能没有提供该功能。

标签: c++ templates namespaces language-lawyer overload-resolution


【解决方案1】:

如果您希望namespace A 中的重载比您必须添加一些东西使其实际上更好。比如说,让它不是模板:

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

否则,实际上没有概念上的理由来解释为什么一个命名空间中的函数模板会优于另一个命名空间中的函数模板,如果两者完全等价的话。毕竟,为什么A 的命名空间中的函数模板会比f 的命名空间中的函数模板“更好”呢? f 的实现者不会“知道得更好”吗?仅仅依靠函数签名可以回避这个问题。

【讨论】:

    【解决方案2】:

    如果您仔细阅读编译器错误,歧义错误不在ns1ns2 中的operator&lt;&lt; 版本之间,而是在ns1operator&lt;&lt;(os, const char*) 实例化和@ 的完全相同的重载之间987654327@。后者在std::ostream 上被 ADL 拖入。

    最好的方法是使用@Barry的推荐,将ns1命名空间中的operator&lt;&lt;去模板化,同时将所有与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)
    }
    

    Live Example

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

    【讨论】:

    • 是和不是。如果你 SFINAE 出字符串,那么cout &lt;&lt; a 在两个函数模板之间仍然会模棱两可
    • 我很想简化 OP 在这方面的示例,将 operator&lt;&lt;(std::ostream&amp;, const T&amp;) 更改为 g(const T&amp;)
    • @Barry print() 可以消除这样的惊喜,是的
    猜你喜欢
    • 1970-01-01
    • 2011-11-14
    • 1970-01-01
    • 2019-01-15
    • 1970-01-01
    • 2014-11-27
    • 1970-01-01
    • 1970-01-01
    • 2020-03-14
    相关资源
    最近更新 更多