Я столкнулся с некоторыми различиями в том, как clang и gcc предупреждают о неиспользуемых переменных.
gcc version 7.3.0 (Ubuntu 7.3.0-27ubuntu1~18.04)
clang version 6.0.0-1ubuntu2
В foo.h
const int f = 3;
В foo.cpp
#include "foo.h"
const int a = 2;
int main() {
int i;
return 0;
}
у меня есть
$ clang -o foo foo.cpp -Wall -Wunused-variable -Wunused-const-variable
foo.cpp:7:9: warning: unused variable 'i' [-Wunused-variable]
int i;
^
foo.cpp:4:11: warning: unused variable 'a' [-Wunused-const-variable]
const int a = 2;
^
2 warnings generated.
$ gcc -o foo foo.cpp -Wall -Wunused-variable -Wunused-const-variable
foo.cpp: In function ‘int main()’:
foo.cpp:7:9: warning: unused variable ‘i’ [-Wunused-variable]
int i;
^
foo.cpp: At global scope:
foo.cpp:4:11: warning: ‘a’ defined but not used [-Wunused-const-variable=]
const int a = 2;
^
In file included from foo.cpp:1:0:
foo.h:1:11: warning: ‘f’ defined but not used [-Wunused-const-variable=]
const int f = 3;
У меня есть несколько вопросов:
Почему gcc жалуется на константу в заголовке? Разве не принято помещать туда константы для ваших клиентов? Как я могу заставить clang вести себя как gcc?
Что, если бы в вашей программе тоже был bar.cpp, который включает foo.h, но не использует f? Вы действительно хотите получить за это предупреждение?





How I can make clang behave like gcc?
Я думаю, только сообщая об этой удивительной ошибке с лязгом и ожидая исправления. (Он все еще присутствует в clang 7).
Единица перевода, определенная вашим foo.cpp, должна быть такой же, как и у файла
производится путем предварительной обработки:
$ clang -E -P foo.cpp >foo.ii
$ cat foo.ii
const int f = 3;
const int a = 2;
int main() {
int i;
return 0;
}
с участием:
$ clang --version
clang version 6.0.1-svn330209-1~exp1~20180427232138.77 (branches/release_60)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
Но clang 6 портит:
$ clang -o foo foo.cpp -Wall -Wunused-variable -Wunused-const-variable
foo.cpp:6:9: warning: unused variable 'i' [-Wunused-variable]
int i;
^
foo.cpp:3:11: warning: unused variable 'a' [-Wunused-const-variable]
const int a = 2;
^
2 warnings generated.
В то время как:
$ clang -o foo foo.ii -Wall -Wunused-variable -Wunused-const-variable
foo.ii:6:9: warning: unused variable 'i' [-Wunused-variable]
int i;
^
foo.ii:1:11: warning: unused variable 'f' [-Wunused-const-variable]
const int f = 3;
^
foo.ii:3:11: warning: unused variable 'a' [-Wunused-const-variable]
const int a = 2;
^
3 warnings generated.
который теперь согласуется с:
$ gcc -o foo foo.ii -Wall -Wunused-variable -Wunused-const-variable
foo.ii: In function ‘int main()’:
foo.ii:6:9: warning: unused variable ‘i’ [-Wunused-variable]
int i;
^
foo.ii: At global scope:
foo.ii:3:11: warning: ‘a’ defined but not used [-Wunused-const-variable=]
const int a = 2;
^
foo.ii:1:11: warning: ‘f’ defined but not used [-Wunused-const-variable=]
const int f = 3;
^
Позже
why does the warning apply to constants in the header when this can be part of a library?
файлы заголовков (и библиотеки) - это не то, что компилятор признает. препроцессор распознает файлы заголовков по:
#include <headername>
...
#include "headername"
Используя заданные пути поиска или пути поиска по умолчанию (-I dir), препроцессор
преобразовывает headername в /some/actual/headername и вставляет содержимое
/some/actual/headername вместо директивы #include в единица перевода
который потребляется компилятором. Эта единица перевода свободна от препроцессора.
директивы. Компилятор не потребляет:
foo.cpp
#include "foo.h"
const int a = 2;
int main() {
int i;
return 0;
}
Потребляет:
foo.ii
const int f = 3;
const int a = 2;
int main() {
int i;
return 0;
}
Наблюдаемое вами поведение clang подразумевает, что внутренне инструмент виртуализирует разграничение между предварительная обработка и составление - что на самом деле является рутинной исторической практикой в реализациях C / C++, но представил это ошибка в виртуальном разграничении. Что бы он ни делал с источником кода, это не совсем то же самое, что сначала его предварительно обработать, а затем скомпилировать вывод препроцессора; а это должно быть.
Таким образом, определение констант в файлах заголовков не является практикой, для которой реализация C++
может оказать любую особую благотворительность. Если вы пишете библиотеку, которая предоставляет константы
в заголовке API, bar.h, и вы не хотите, чтобы пользователи этой библиотеки подвергались риску
предупреждения о неиспользуемых переменных из-за отсутствия ссылки на каждую константу, определенную в
bar.h в каждой компиляции, в которую его входит #include, затем вы определяете не буду
константы просто переменные в видеconst в bar.h. Вы сделаете еще одно из трех:
Определите константы как члены enum или enum class:
enum class E : int {
F = 3
//...
};
Или, объявлять константы extern в bar.h, но определять их в исходном файле библиотеки 1:
bar.h
#ifndef BAR_H
#define BAR_H
extern const int f;
#endif
bar.cpp
#include "bar.h"
const int f = 3;
Или определите константы как макросы препроцессора:
#define F 3
в стиле старой школы C. Чего вы не сделаете, потому что в C++ мы избегаем препроцессора. Если мы можем.
extern предотвращает предупреждение? Поскольку переменные области видимости файла const
неявно static в C++ (но не в C), и компилятор никогда не учитывает
переменная extern, подходящая для диагностики неиспользованный, потому что вы сообщаете
это то, что на переменную можно ссылаться в коде, предоставленном компоновщику
что компилятор не может видеть.
Вероятно, отметит это как правильное. Но у меня есть другой вопрос - почему предупреждение применяется к константам в заголовке, если это может быть частью библиотеки? Или это предполагаемое поведение unused-const-variable?
@IvanKalchev См. Расширенный ответ.
Неиспользуемая переменная часто означает ошибку где-то, отсюда и предупреждения компилятора. Разные компиляторы предупреждают по-разному (очевидно, спецификация не определяет это поведение).