ADL应该如何为此工作?

时间:2014-09-23 16:52:06

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


最近我遇到了一个clang ++ 5.0.0编译器的问题,其中通过ADL它没有在Mac上找到正确的函数(但是g ++在Linux上正确地完成了它)。我想知道它是一个编译器问题还是一般的穷人类设计。
这是一个示例代码(纯粹用于说明目的):

namespace test {
    class Ops {
      public:
        Ops():val_(0){}
        template<typename T>
        Ops& operator<< (const T& val) {
            std::cout << "Called member function" << std::endl;
            this->val_ = val;
            return *this;
        }
      private:
        int val_;
    };

    template<typename T>
    struct Any {
        T val_;
    };

    template <template<typename> class E,  typename T>
    Ops& operator<< (Ops& op, const E<T>& val) {
        std::cout << "Global function" << std::endl;
        return op;
    }
}

int main() {
    test::Ops op;
    int k = 9;
    test::Any<int> a;
    op << a;

    return 0;
}

我想知道ADL和模板参数推导如何以步进方式找到最佳匹配? 对于同一个主体,是否会出现任何情况?成员函数将取代自由函数? (这就是产品构建中发生的事情)

提前致谢。

2 个答案:

答案 0 :(得分:2)

这是详细发生的事情以及每个编译器应该做的事情:通过限定查找找到候选模板函数

template <typename T>
test::Ops::operator<<(const T&)

而第二个候选人是通过ADL使用模板参数推导生成的(cfr.temp.deduct.conv)

template <template <typename> class E, typename T>
test::operator<<(test::Ops&, const E<T>&)

之后超载解决方案开始(cfr.13.3.3),非成员一(F1)优先于成员(F2),因为

  
      
  • F1和F2是功能模板专精,F1的功能模板更专业   根据14.5.6.2中描述的部分排序规则,F2的模板。
  •   

因此被选为要调用的函数。

回答你的问题:这取决于重载解决规则。作为成员函数或在内部范围内不会影响结果,例如

namespace test {
    class Ops {
      public:
        Ops():val_(0){}

        template<typename T>
        Ops& operator<< (const T& val) {
            std::cout << "Called member function" << std::endl;
            this->val_ = val;
            return *this;
        }

      private:
        int val_;
    };

    template<typename T>
    struct Any {
        T val_;
    };

    template <typename E>
    Ops& operator<< (Ops& op, const E& val) {
        std::cout << "Global function" << std::endl;
        return op;
    }
}

只会触发重载解析错误&#39; use of overloaded operator '<<' is ambiguous&#39;。

作为一个加号:成员函数即使被选中也是错误的:this->val被赋予非整数类型。

答案 1 :(得分:1)

这两个候选函数位于重载集中:

// member function template, found by qualified lookup
template <typename T>
test::Ops::operator<<(const T&)

// non-member function template, found by ADL
template <template <typename> class E, typename T>
test::operator<<(test::Ops&, const E<T>&)

在操作员查找中,不会优先考虑成员与非成员。在模板参数替换之后,两个函数模板特化与提供的参数类型完全匹配(具有限定转换)。但是采用E<T>的函数比采用T更加专业化,因此选择非成员函数。

Apple clang 5.0.0基于LLVM clang 3.3svn。我找不到任何选择成员函数的LLVM clang版本。它可能是Apple代码中的一个错误,但恕我直言,它更可能是您实际编译的代码或您的环境中的一些细微差别。您是否尝试使用可疑编译器编译示例代码?