Подтвердить что ты не робот

Невозможно объявить оператор внутри функции. Ошибка Clang или spec?

Один из странных угловых случаев C состоит в том, что функции могут быть объявлены в рамках других функций, например.

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

Это перенесено на С++, по крайней мере для большинства функций. 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 составляют этот штраф. Проверка имени в объекте предполагает, что оператор == был объявлен с правильными типами. Clang (я пробовал до 3.8):

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

Если объявление не будет перемещено непосредственно над функцией, в этом случае Clang также будет счастлив:

bool operator==(const foo &, const foo &);
bool wot(const bar &x, const bar &y)
{
  return x.value == y.value; // fine
}

Я не могу использовать это обходное решение, так как случай "реального мира", вызвавший этот вопрос, включает в себя слои шаблонов, то есть я знаю только имя типа "foo" в объявлении функции.

Я считаю, что это ошибка в Clang - есть ли специальная обработка бесплатных функций operatorX, которая запрещает объявление их внутри функции?

4b9b3361

Ответ 1

Для перегруженных операторов см. [over.match.oper]/(3.2):

[...] для двоичного оператора @ с левым операндом типа, cv-неквалифицированная версия которого T1 и правый операнд типа, cv-unqualified версия T2, [...] non-member кандидаты [...] построены следующим образом:

Набор кандидатов, не являющихся членами, является результатом неквалифицированного поиск [email protected] в контексте выражения в соответствии с обычные правила поиска имен в неквалифицированных вызовах функций (3.4.2) за исключением того, что все функции-члены игнорируются. Однако, если никакой операнд имеет тип класса, [...]

То есть, мы имеем точные правила поиска одинакового имени, как в обычных вызовах, потому что x.value имеет тип класса (foo). Это ошибка Clang, и она встречается со всеми бинарными операторами. Подано как # 27027.

Ответ 2

Верно, что C позволяет объявлять функции внутри функций: 6.7.5.3 Объявление функций §17 (проект n1256 для C99) говорит (подчеркивайте мое)

Если декларация происходит вне любой функции, идентификаторы имеют размер файла и внешнюю привязку. Если объявление происходит внутри функции, идентификаторы функций f и fip имеют масштаб блока и либо внутренняя или внешняя связь (в зависимости от того, какие видимые области видимости для этих идентификаторов видны) и идентификатор указателя pfi имеет область видимости блока и отсутствие привязки.

С++ также позволяет им. В проекте n4296 для С++ 14 говорится:

13.2 Согласование соответствия [over.dcl]
...
2 Локально объявленная функция не входит в ту же область действия, что и функция в содержащей области. [Пример:
void f (const char *);
void g() {
extern void f (int);
...

(приведенная цитата только здесь, чтобы иметь явное доказательство того, что С++ допускает объявления функций внутри функции)

Я мог бы даже проверить, что с вашим примером эта строка:

bool op2 = operator == (x.value, y.value);

компилируется без единого предупреждения и дает ожидаемые результаты.

Итак, я бы сказал, что это ошибка в Clang