【问题标题】:Templates and argument dependent lookup模板和参数依赖查找
【发布时间】:2011-04-20 18:14:11
【问题描述】:

在编译这个程序时,我希望 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]

【问题讨论】:

    标签: c++ namespaces argument-dependent-lookup


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

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

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

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

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

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

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


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

      【讨论】:

        【解决方案3】:

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

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

        使用 IOStreams 库时,此问题通过接受 istream &amp;ostream &amp; 类型的参数解决,无需模板参数化。

        【讨论】:

          【解决方案4】:

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

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

          【讨论】:

            猜你喜欢
            • 2016-03-24
            • 1970-01-01
            • 2011-11-02
            • 2019-10-26
            • 2014-06-07
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多