clang не предупреждает о «определенном, но не используемом» в заголовке, gcc делает - PullRequest
0 голосов
/ 04 декабря 2018

Я столкнулся с некоторыми различиями в том, как 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?

1 Ответ

0 голосов
/ 04 декабря 2018

Как заставить 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 подходящей для диагностики неиспользованных , потому что вы говорите, что переменная может бытьссылка в коде, предоставленном компоновщику, который не может видеть компилятор.
...