Clang не предупреждает об "определено, но не используется" в заголовке, gcc делает

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

Неиспользуемая переменная часто означает ошибку где-то, отсюда и предупреждения компилятора. Разные компиляторы предупреждают по-разному (очевидно, спецификация не определяет это поведение).

PlinyTheElder 04.12.2018 20:23

Что, если бы в вашей программе тоже был bar.cpp, который включает foo.h, но не использует f? Вы действительно хотите получить за это предупреждение?

M.M 05.12.2018 01:51
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
2
525
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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++ мы избегаем препроцессора. Если мы можем.


[1] Как extern предотвращает предупреждение? Поскольку переменные области видимости файла const неявно static в C++ (но не в C), и компилятор никогда не учитывает переменная extern, подходящая для диагностики неиспользованный, потому что вы сообщаете это то, что на переменную можно ссылаться в коде, предоставленном компоновщику что компилятор не может видеть.

Вероятно, отметит это как правильное. Но у меня есть другой вопрос - почему предупреждение применяется к константам в заголовке, если это может быть частью библиотеки? Или это предполагаемое поведение unused-const-variable?

Ivan Kalchev 05.12.2018 08:54

@IvanKalchev См. Расширенный ответ.

Mike Kinghan 05.12.2018 11:25

Другие вопросы по теме