最近我遇到了一个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和模板参数推导如何以步进方式找到最佳匹配?
对于同一个主体,是否会出现任何情况?成员函数将取代自由函数? (这就是产品构建中发生的事情)
提前致谢。
答案 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代码中的一个错误,但恕我直言,它更可能是您实际编译的代码或您的环境中的一些细微差别。您是否尝试使用可疑编译器编译示例代码?