中缀vs前缀语法:名称查找差异

时间:2014-08-17 19:03:09

标签: c++ syntax operator-overloading iostream

C ++中的运算符通常被认为是函数/方法的替代语法,尤其是在重载的上下文中。如果是这样,下面的两个表达式应该是同义词:

std::cout << 42;
operator<<(std::cout, 42);

实际上,第二个语句会导致以下错误:

call of overloaded ‘operator<<(std::ostream&, int)’ is ambiguous

像往常一样,这样的错误信息附有可能的候选人名单,这些是:

operator<<(basic_ostream<_CharT, _Traits>& __out, char __c)
operator<<(basic_ostream<char, _Traits>& __out, char __c)
operator<<(basic_ostream<char, _Traits>& __out, signed char __c)
operator<<(basic_ostream<char, _Traits>& __out, unsigned char __c)

这样的错误引发了至少两个问题:

  1. 这两个语句在哪些方面有所不同(就名称查找而言)?
  2. 为什么遗漏了operator<<(basic_ostream<char, _Traits>& __out, int __c)
  3. 看起来,中缀和前缀符号不是完全可互换的 - 不同的语法需要不同的名称解析策略。有什么区别,它们来自哪里?

2 个答案:

答案 0 :(得分:15)

不,这两个表达式不应该是同义词。 std::cout << 42operator<<(std::cout, 42)std::cout.operator<<(42)查找。两种查找都可以产生可行的候选者,但第二种是更好的匹配。

答案 1 :(得分:2)

这些是C ++ 17 [over.match.oper / 3]中的运算符查找规则,为简洁起见,我删除了与重载operator<<无关的文本,其中左操作数是一个类,类型;并加粗了一部分,我将在后面解释:

  

对于二进制运算符@,其左操作数类型为cv不合格版本为T1,而右操作数类型为cv不合格版本为T2,则三个候选函数集,指定为成员候选非成员   候选内置候选,其构造如下:

     
      
  • 如果T1是完整的类类型或当前正在定义的类,则候选成员集是对T1::operator@(16.3.1.1.1)进行合格查找的结果;否则,成员候选人集为空。
  •   
  • 根据在不合格函数调用中进行名称查找的通常规则,在表达式的上下文中,operator@的不合格查找结果是struct X{ operator int(); }; void f(X); struct A { void f(int); void g() { X x; f(x); } // Calls A::f }; 的结果,除非所有成员函数都是忽略。
  •   

内置候选在此处为空,表示搜索函数会将两个操作数隐式转换为整数类型并应用位移运算符;但是没有从iostreams到整数类型的隐式转换。

  

用于重载解决方案的候选函数集是成员候选者,非成员候选者和内置候选者的并集。


使运算符查找与其他函数查找不同的原理是什么?这一切意味着什么?我认为最好通过几个例子来回答。首先:

f(x)

在此示例中,有一个原则:如果您尝试从类的另一个成员函数中调用该类的成员函数;它肯定应该找到该成员函数,并且不会受到外部函数(甚至包括ADL)的污染。

因此,部分不合格的查找规则是,如果查找的非ADL部分找到了类成员函数,则不会执行ADL。

如果没有该规则,A::f会同时找到::f::f,然后重载分辨率会选择struct X{}; std::ostream& operator<<(std::ostream&, X); struct S { std::ostream& operator<<(int); void f() { X x; std::cout << x; // OK // operator<<(std::cout, x); // FAIL // std::cout.operator<<(x); // FAIL } }; 作为更好的匹配,这是我们不希望的。

在第二个示例中:

std::cout << 42;

根据前面示例的原理-如果规则只是将operator<<(std::cout, 24);转换为S::operator<<,则名称查找将找到OK并停止。哎呀!

因此,我认为说上面的FAIL行的行为来自于标记为std::cout << x;的两行都是不正确的,正如其他答案/评论所建议的那样。


摘要:

现在我们可以在我的答案顶部了解标准报价的实际用语了。

代码std::cout.operator<<(x);将:

  • operator<<(std::cout, x)的身份 AND
  • 查找为SELECT estudiante.nombre, COUNT(materia.nombre) FROM `estudiante` INNER JOIN materiaestudiante ON materiaestudiante.idEstudiate = estudiante.id INNER JOIN materia ON materia.id = materiaestudiante.idMateria GROUP BY estudiante.nombre HAVING COUNT(materia.nombre) > 2 ,只是成员函数被忽略(因此,由于找到了成员函数,因此没有ADL抑制)。

然后对这两个集合的并集执行重载解析。