以下代码无法在我的Visual Studio 2019中编译。但是,如果我删除>>的第一个重载,它将进行编译。 该代码可以在g ++中编译,这使我感到困惑。我猜编译器生成模板函数是在不同的位置?
typedef std::vector<int> Mon; // ordered
typedef std::vector<Mon> Poly; // ordered
class A {};
// It would compile successfuly if this function is removed
std::istream& operator>>(std::istream& sin, A& a)
{
return sin;
}
template <typename Container>
void load(std::istream& sin, Container& cont)
{
typename Container::value_type n;
sin >> n;
}
std::istream& operator>>(std::istream& sin, Mon& mon)
{
load(sin, mon);
return sin;
}
std::istream& operator>>(std::istream& sin, Poly& poly)
{
load(sin, poly);
return sin;
}
int main()
{
return 0;
}
答案 0 :(得分:2)
潜在的问题是此函数在全局名称空间中签名:
std::istream& operator>>(std::istream& sin, std::vector<int>& mon);
不能通过依赖于参数的查找来找到。由于所有参数都位于std
中,因此ADL仅搜索std
而不搜索全局名称空间。
为避免此类问题,您可以遵循一条经验法则:不要以ADL无法找到的方式重载运算符。 (推论:您不应该尝试使vector<Foo> v; cin >> v;
有效)。
首先,请注意,语法sin >> n
转换为同时执行operator>>(sin, n)
和sin.operator>>(n)
并合并所有结果as described in full here。
问题中的代码与this question非常相似,我将在此处总结最佳答案的发现。
此功能:
template <typename Container>
void load(std::istream& sin, Container& cont)
{
typename Container::value_type n;
sin >> n;
}
特别是在发生operator>>(sin, n)
查找时,operator>>
是一个依赖名称,因为它是函数调用的名称,其参数类型取决于模板参数。 / p>
将名称查找应用于从属函数名称(参考:[temp.dep.candidate])时,规则为:
extern
个函数,如果它们在实例化点具有可见的声明,则ADL会发现它们,而这些额外的声明会影响重载分辨率,则该程序行为不确定(无需诊断)。(注意:我的第一版答案错误地引用了规则3,因此得出了错误的结论):
因此,由于找到了成员函数sin >> n
,因此从调用load(sin, mon);
实例化的std::istream::operator>>(int&)
查找成功。 (搜索还找到了A&
版本,但是重载分辨率选择了成员函数。)
问题由sin >> n
实例化的load(sin, poly);
查找而引起。
根据规则1,找到operator>>(std::istream&, A&)
。 (此函数稍后将被重载解析丢弃,但是在此阶段,我们仅执行名称查找)。
根据规则2,ADL名称空间列表为:std
。因此,此步骤将找到std::operator>>
(各种重载),但找不到::operator>>(istream&, Mon&);
,因为它不在命名空间std
中。
第3条不适用,因为namespace std
中没有任何可以接受Mon
的重载。
所以正确的行为是:
sin >> n
时load(sin, poly);
不匹配,因此发布的代码将无法编译。It would compile successfully if this function is removed
的行实际上应该没有区别;出于相同的原因,代码仍将无法编译。结论:在我看来:
我注意到,如果我们将operator>>
更改为bar
,将sin >> n
更改为bar(sin, n);
,则gcc和msvc会正确拒绝这两个版本。 gcc甚至给出了与clang非常相似的错误消息。
因此我推测该错误可能是overloaded operator name lookup rules的不正确应用-与非操作员名称略有不同,但与此代码示例无关。
有关这些规则的原理和MSVC行为的深入介绍,请参见this excellent article。