无法在函数内声明运算符。是Clang的bug还是规范问题?

19

C语言中比较奇特的一种情况是函数可以在其他函数内部声明,例如:

void foo(void)
{
  void bar(void); // Behaves as if this was written above void foo(void)
  bar();
}

这种情况也出现在C++中,至少对于大部分函数是如此。如果所涉及的函数被称为operator==,Clang似乎无法识别该模式。

struct foo
{
  int value;
};

struct bar
{
  foo value;
};

bool wot(const bar &x, const bar &y)
{
  bool eq(const foo &, const foo &);         // Declare function eq
  bool operator==(const foo &, const foo &); // Declare function operator==
  bool func = eq(x.value, y.value);          // This line compiles fine
  bool call = operator==(x.value, y.value);  // Also OK - thanks user657267!
  bool op = x.value == y.value;              // This one doesn't
  return func && call && op;
}

bool test()
{
  bar a;
  bar b;
  return wot(a,b);
}

GCC和ICC编译可以正常通过。在对象中检查名称混淆表明operator==已经用正确的类型声明。Clang(我尝试了3.8版本)报错:

error: invalid operands to binary expression
      ('const foo' and 'const foo')
      bool op = x.value == y.value;
                ~~~~~~~ ^  ~~~~~~~

除非将声明移动到函数的正上方,否则Clang会报错。如果将声明移动到函数正上方,Clang就不会报错了。
bool operator==(const foo &, const foo &);
bool wot(const bar &x, const bar &y)
{
  return x.value == y.value; // fine
}

我无法使用这个解决方法,因为引发这个问题的“真实世界”情况涉及多层模板,这意味着我只知道函数声明中的类型名称“foo”。

我认为这是Clang中的一个bug - 是否有operatorX自由函数的特殊处理方式,禁止在函数内部声明它们?


3
使用明确的调用 bool op = operator==(x.value, y.value); 看起来工作正常,但出于某种原因,Clang 的操作符查找未能找到块作用域声明。 - user657267
非常好的发现!谢谢你。 - Jon Chesterfield
[over.match.oper]/2 表示操作符调用 a@b 会在任何其他事情发生之前引起一个非成员候选函数 operator@(a,b),所以第二个形式有效而第一个不是则强烈表明存在一个错误。 - M.M
2个回答

7

有关重载运算符,请参见[over.match.oper]/(3.2)

[...]对于左操作数的cv-unqualified版本为T1,右操作数的cv-unqualified版本为T2的二元运算符@,构造非成员候选者如下:

非成员候选者集是根据未限定函数调用中名称查找的通常规则,在表达式的上下文中查找operator@的结果,但忽略所有成员函数。 但是,如果没有操作数具有类类型,[...]

也就是说,我们具有与普通调用相同的名称查找规则,因为x.value是类类型(foo)。这是一个Clang bug,并且适用于所有二进制运算符。提交了#27027


2
在C语言中,函数可以在其他函数内部声明,根据C99标准的6.7.5.3函数声明符第17条(我强调的):如果声明发生在任何函数之外,则标识符具有文件作用域和外部链接性。如果声明发生在函数内部,则f和fip函数的标识符具有块作用域并且具有内部或外部链接性(取决于对这些标识符的文件作用域声明是否可见),指针pfi的标识符具有块作用域且没有链接性。C++也允许函数在函数内部声明,根据C++14标准的n4296草案,13.2声明匹配[over.dcl]... 2当地声明的函数不在包含范围内的函数相同的作用域。 (上述引用只是为了明确证明C ++允许在函数内部声明函数)。
bool op2 = operator == (x.value, y.value);

编译没有任何警告,结果符合预期。

所以我认为这是Clang的一个bug。


1
13.2 引语的相关性是什么?在 OP 代码中,无论如何都没有包含范围内相同名称的函数。 - M.M
@M.M 这只是为了明确证明C++允许在函数内部声明函数。 - Serge Ballesta
好的。[basic.link]/6 和/7 实际上描述了块作用域函数声明的匹配规则。 - M.M

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接