Как заставить Clang вести себя как gcc?
Я думаю только, сообщив об этой удивительной ошибке clang и ожидая исправления.(Он все еще присутствует в 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
Но лязг 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;
^
Позже
почему предупреждение применяется к константам в заголовке, когда оно может быть частью библиотеки?
файлы заголовков (и библиотеки ) не являются вещами, которые распознает компилятор.Препроцессор распознает заголовочные файлы следующим образом:
#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;
}
Наблюдаемое вами поведение лязга подразумевает, что внутренне инструмент виртуализирует разграничение между предварительной обработкой и компиляцией - что на самом делеобычная историческая практика в реализациях C / C ++ - но эта ошибка была введена при виртуальном разграничении.Что бы он на самом деле ни делал с исходным кодом, это не то же самое, что сначала его предварительная обработка, а затем компилирование выходных данных предварительной обработки; должно быть .
Так что определение констант в заголовочных файлах не является практикой, на которую реализация C ++ может распространить любую специальную благотворительность.Если вы пишете библиотеку, которая предоставляет константы в своем заголовке API, bar.h
, и вы не хотите, чтобы пользователи этой библиотеки подвергались риску предупреждений о неиспользуемых переменных из-за невозможности ссылаться на каждую константу, определенную в bar.h
в каждомкомпиляция, что #include
s, то вы не будете определять эти константы просто как 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 ++ мы избегаем препроцессора, если можем.
[1] Как
extern
предотвращает предупреждение?Потому что
const
переменные файловой области неявно
static
в C ++ (хотя и не в C), и компилятор никогда не считает переменную
extern
подходящей для диагностики
неиспользованных , потому что вы говорите, что переменная может бытьссылка в коде, предоставленном компоновщику, который не может видеть компилятор.