2

在编译这个程序时,我期望 operator<< 调用解析为全局命名空间中的那个,但是编译器却报告了一个模棱两可的重载。我认为非依赖查找发生在命名空间中的函数之前,这些函数由于参数依赖查找而被包含为潜在匹配项。这似乎是非模板函数的情况。

有人可以解释吗?

#include <iostream>

class Foo
{};

namespace NS
{
    class Stream
    {};

    template <typename T>
    Stream& operator << ( Stream& s, T t)
    {
        std::cerr << "Namespace call!\n";
        return s;
    }
}

template<typename STREAM>
STREAM& operator << ( STREAM& s, Foo f )
{
    std::cerr << "Global NS call";
    return s;
}

/**
* This function (as opposed to the one above) is not ambiguous.  Why?

NS::Stream& operator << ( NS::Stream& s, Foo f )
{
    std::cerr << "Global NS call";
    return s;
}

*/

int main()
{
    Foo f;
    NS::Stream s;

    s << f;
    return 0;
}

编译器输出:

test11.cpp: In function ‘int main()’:
test11.cpp:28: error: ambiguous overload for ‘operator<<’ in ‘s << f’
test11.cpp:18: note: candidates are: STREAM& operator<<(STREAM&, Foo) [with STREAM = NS::Stream]
test11.cpp:13: note:                 NS::Stream& NS::operator<<(NS::Stream&, T) [with T = Foo]
4

4 回答 4

3

有两种可能的候选者s << f:全局的和命名空间的。对于 C++ 编译器,这两者之间没有什么可以选择的,所以它是模棱两可的。

于 2011-04-20T18:16:21.117 回答
2

尽管这是一个老问题,但我认为关于 OP 的具体问题仍有一些尚未澄清的事情,所以我们开始吧。

首先:依赖于参数的查找和正常的不合格查找都是针对不合格的函数和运算符调用进行的。这适用于普通函数和函数模板特化。根据 [3.4.2 第 3 段],唯一的例外是正常的不合格查找发现:

  • 类成员的声明,或
  • 不是 using 声明的块范围函数声明,或
  • 既不是函数也不是函数模板的声明。

仅在上述情况下,不执行与参数相关的查找。如您所见,这些都不适用于这种情况。

因此,找到了两个声明。现在,需要执行重载决议以选择最佳可行函数。在这两种情况下,参数都非常适合参数类型,因此不能根据更好的转换选择一个重载而不是另一个重载。两者都是模板特化,因此,作为最后的手段,函数模板的部分排序用于尝试确定一个是否比另一个更特化。唉,NS 中的模板更专门用于第一个参数,而全局模板更专门用于第二个参数,因此没有模板比另一个模板更专门。结论:没有重载可以选择其他,调用是模棱两可的。

现在,对于您的第二个问题,关于已注释掉的运算符定义。如果您取消注释该定义,在这种情况下,也会执行 ADL;所有三个重载都是通过名称查找找到的。同样,所有参数都与参数类型完美匹配。不同的是最后一个定义是一个普通的操作符函数,而不是一个模板。如果不能根据转换选择重载而不是其他重载,那么,如果一个是普通函数并且所有其他都是模板特化,则非模板的优先于其他。这就是为什么在这种情况下调用不再模棱两可的原因。


标准参考是 N4140,这是发布前的最后一个 C++14 草案,但我认为自 C++03 以来上述任何内容都没有改变。

于 2015-01-04T17:39:41.180 回答
1

全局命名空间没有特殊的优先级。问题s << f在于两个参数都与命名空间相关联:swith::NSfwith ::

鉴于全局命名空间与其他命名空间一样(除了它始终在范围内,这在此处无关紧要),这两个函数重载完全绑定为最佳匹配,编译器无能为力。

当使用 IOStreams 库时,这个问题可以通过接受istream &or类型的参数来解决ostream &,而无需模板参数化。

于 2011-04-20T18:57:58.093 回答
0

有歧义,因为你已经定义Streaminside namespace NS。如果您Stream在全局命名空间中定义,则不会有歧义。

编译器将尝试根据非限定函数的参数及其关联的命名空间来解析选择哪个函数。请参阅 ISO/IEC 14882:2003 标准的第 3.4.2 节 - 参数相关名称查找。由于一个参数定义在全局命名空间中,一个参数定义在 NS 中,编译器不知道使用哪个函数。

于 2011-04-20T18:23:28.097 回答