Почему я получаю предупреждения о том, что функция используется, но не определена и не определена, но не используется? - PullRequest
4 голосов
/ 31 января 2020

Я столкнулся с необычной парой предупреждений компилятора, которые кажутся взаимно противоречивыми. Вот код, который я написал:

#include <functional>
#include <iostream>

namespace {
  std::function<void()> callback = [] {
    void theRealCallback(); // Forward-declare theRealCallback
    theRealCallback();      // Invoke theRealCallback
  };

  void theRealCallback() {
    std::cout << "theRealCallback was called." << std::endl;
  }
}

int main() {
  callback();
}

Другими словами, я определяю лямбда-функцию в безымянном пространстве имен, которая затем объявляет функцию, определенную позже в этом безымянном пространстве имен.

Когда Я компилирую этот код с g ++ 7.4.0 (Ubuntu 7.4.0-1ubuntu1 ~ 18.04.1), я получаю эти два предупреждения:

CompilerWarningsNamespace.cpp:6:10: warning: ‘void {anonymous}::theRealCallback()’ used but never defined
     void theRealCallback();
          ^~~~~~~~~~~~~~~
CompilerWarningsNamespace.cpp:10:8: warning: ‘void {anonymous}::theRealCallback()’ defined but not used [-Wunused-function]
   void theRealCallback() {
        ^~~~~~~~~~~~~~~

Это странно, потому что первое предупреждение говорит, что я использование функции без ее определения, а второе предупреждение говорит о том, что я определяю функцию без ее использования.

Запуск этой программы действительно дает вывод

theRealCallback was called.

, и программа нормально завершается .

Интересно, что эти предупреждения go исчезнут, если вместо использования безымянного пространства имен я присваиваю пространству имен имя, как показано здесь:

#include <functional>
#include <iostream>

namespace NamedNamespace {
  std::function<void()> callback = [] {
    void theRealCallback();
    theRealCallback();
  };

  void theRealCallback() {
    std::cout << "theRealCallback was called." << std::endl;
  }
}

int main() {
  NamedNamespace::callback();
}

Модифицированная версия кода, как и оригинальный код, распечатывает сообщение о том, что был вызван theRealCallback.

Может кто-нибудь объяснить, почему я получаю эти предупреждения? Я предполагаю, что это как-то связано с предварительным объявлением функции в лямбда-функции как интерпретируемой как что-то отличное от более поздней функции, которая появляется в безымянном пространстве имен, но если это так, я не уверен, что вижу почему это ссылки в конце и почему я получаю эти предупреждения.

1 Ответ

4 голосов
/ 31 января 2020

Я думаю, что открытый CWG выпуск 2058 здесь важен для определения правильности вашей программы.


В соответствии с нынешней формулировкой стандарта, я думаю, что ваша программа неправильно сформирован.

На основе стандарта C ++ 17 (окончательный вариант):

Согласно [basi c .link] / 6 декларации в вашей лямбде будет объявлено theRealCallback с внешней связью , потому что это объявление функции в области видимости блока, которое не соответствует какой-либо другой, уже объявленной сущности.

В то же время время согласно [basi c .link] /4.2 второе объявление theRealCallback имеет внутреннюю связь , поскольку это объявление области имен пространства функций в безымянном пространстве имен.

Согласно [basi c .link] / 6 программа плохо сформирована, если она объявляет сущность с внутренними и внешними связями в одной и той же единице перевода. Эта неправильная форма была добавлена ​​только недавно, в качестве разрешения CWG, выпуск 426 .


Как упоминалось в примечаниях к CWG, выпуск 426, согласно [basi c .link] / 9 , объявления ссылаются только на одну и ту же сущность, хотя, если они имеют одинаковую связь, то есть условие неформальности в ее резолюции не применяется.

Так что если мы интерпретируйте это строго, тогда программа действительно имеет две независимые функции void theRealCallback(), одна с внешней и одна с внутренней связью. Тот, у которого есть внутренняя связь, имеет определение, а тот, у которого нет внешней связи. В этом случае программа нарушает правило одного определения, поскольку при вызове theRealCallback(); в лямбда-ODR используется функция с внешней связью, у которой нет определения. Это сделало бы программу некорректной, диагностика не требовалась c.

Хотя это, вероятно, правильное толкование при строгом чтении стандарта, я думаю, что решение проблемы CWG 426 предназначалось для применения здесь потому что с интерпретацией выше это никогда не будет применяться. Я не знаю, почему упомянутая проблема не была исправлена ​​в резолюции.


Если проблема 1058 * CWG 2058 должна быть решена, чтобы сказать, что объявление в области видимости блока будет соответствовать связи окружающего пространства имен, тогда программа будет правильно сформирована, а theRealCallback будет иметь внутреннюю связь.


Вы можете видеть, что G CC считает программу плохо сформированной, если стандарт интерпретируется строго путем добавления флага -pedantic-errors, который заставит его выдавать ошибку, а не предупреждение.


Конечно, самое простое решение, позволяющее обойти эту проблему, - это объявить вперед void theRealCallback(); вне лямбды в области пространства имен, в этом случае связь определенно является внутренней, и любое объявление области блока будет ссылаться на это объявление, принимая его связь.


Это не проблема, если пространство имен именуется, потому что тогда объявление области имен будет иметь внешнюю связь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...