模板和自变量相关的查找

| 编译该程序时,我期望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]
    
已邀请:
尽管这是一个古老的问题,但我认为关于OP的特定问题仍有一些事情尚待澄清,因此我们继续。 首先:对不合格的函数和操作员调用都进行了依赖于参数的查找和常规不合格的查找。这适用于常规功能和功能模板专门化。根据[3.4.2第3段],唯一的例外是如果发现正常的不合格查询: 集体成员的声明,或 不是使用声明的块范围函数声明,或者 既不是函数也不是函数模板的声明。 仅在以上情况下,不执行依赖于参数的查找。如您所见,在这种情况下,这些都不适用。 因此,找到了两个声明。现在,需要执行重载解析以选择最佳可行功能。参数在两种情况下都完全适合参数类型,因此,基于更好的转换,不能选择一个重载而不是另一个重载。两者都是模板专业化,因此,作为最后的手段,功能模板的部分排序用于确定一个模板是否比另一个专业。 NS,NS中的模板对第一个参数更专门,而全局模板对第二个参数更专业,因此没有模板比另一个更专业。结论:不能选择任何重载,这是模棱两可的。 现在,关于第二个问题,关于已注释掉的运算符定义。如果取消注释该定义,在这种情况下,也会执行ADL;否则,将执行ADL。所有三个重载都通过名称查找找到。同样,所有参数都与参数类型完全匹配。不同之处在于最后一个定义是普通的运算符函数,而不是模板。如果无法基于转换选择过载,则如果一个过载是正常函数,而所有其他过载都是模板特化,则非模板优先于其他。这就是在这种情况下呼叫不再歧义的原因。 标准参考是N4140,它是发布前的最新C ++ 14草案,但我认为自C ++ 03以来,上述任何更改都没有。     
s << f
有两种可能的候选者:全局候选者和命名空间候选者之一。对于C ++编译器,在这两者之间没有选择可供选择,因此它是模棱两可的。     
全局名称空间没有特殊的优先级。
s << f
的问题是两个参数都与一个命名空间相关联:
s
::NS
f
::
。 鉴于全局名称空间与其他名称空间一样(除了它始终在作用域内,在这里无关紧要),这两个函数重载已完全绑定在一起,以实现最佳匹配,并且编译器无法执行任何操作。 使用IOStreams库时,可以通过接受类型为“ 8”或“ 9”的参数来解决此问题,而无需进行模板参数化。     
由于在
namespace NS
中定义了
Stream
,因此存在歧义。如果在全局名称空间中定义“ 10”,则不会有歧义。 编译器将尝试根据非限定函数及其关联的命名空间的参数来解析选择哪个函数。请参阅ISO / IEC 14882:2003标准的第3.4.2节-与参数相关的名称查找。由于在全局名称空间中定义了一个参数,而在NS中定义了一个参数,因此编译器不知道要使用哪个函数。     

要回复问题请先登录注册