При компиляции исходного файла C++, если у вас есть несколько заголовков, состоящих из заключенных в скобки и цитируемых заголовков, всегда ли заголовки в квадратных скобках должны быть указаны первыми?
Я поместил заголовок в кавычки перед заголовком в квадратных скобках, и это начало давать мне ошибки компилятора переопределения.
Заголовочный файл (utilities.h):
#ifndef _UTILITIES_H
#define _UTILITIES_H
#include <vector>
double calcNorm(std::vector <double> array); //template?
#endif
Исходный файл (utilities.cpp)
#include <math.h>
#include <vector>
#include "utilities.h"
double calcNorm(std::vector <double> array) //template?
{
int n;
double norm = 0;
n = array.size();
for (int i = 0; i < n; i++)
{
norm += array[i]*array[i];
}
norm = sqrt(norm);
return norm;
}
Раньше в исходном файле у меня был #include "utilities.h"
над заголовками в квадратных скобках, и я получал следующую ошибку компилятора:
g++ -g -Wall -c utilities.cpp
utilities.cpp: In function ‘double calcNorm(std::vector<double>)’:
utilities.cpp:5:8: error: redefinition of ‘double calcNorm(std::vector<double>)’
double calcNorm(std::vector <double> array) //template?
^
utilities.h:3:8: note: ‘double calcNorm(std::vector<double>)’ previously defined here
makefile:12: recipe for target 'utilities.o' failed
Если вы не делаете ничего глупого и правильно включаете свои зависимости в каждый заголовок, то порядок включения не имеет значения. Но чтобы ответить на этот вопрос, в чем именно заключается ошибка и как выглядит файл заголовка? Я подозреваю, что вы using namespace std;
что плохо вызываете конфликт имен, но вы должны показать код, чтобы быть уверенным
Чтобы быть ясным, MVCE относится к Минимальный, полный, проверяемый пример, который поможет сделать ваш вопрос доступным для ответа и который имеет то преимущество, что, вероятно, найдет ошибку за вас. Пожалуйста, редактировать ваш вопрос, чтобы включить его.
Мое практическое правило - сначала стандартные заголовки, затем заголовки библиотек, а затем мои собственные заголовки. Это упрощает поиск ошибок в моих заголовках. Однако это не обязательно.
@NathanOliver всегда всегда наоборот!
Похоже, что в одном из цитируемых вами заголовков есть что-то плохое.
Извините. Я только что обновил OP файлами, вызывающими эту ошибку.
_UTILITIES_H
- не самое уникальное имя. Также IIRC это может вызвать неопределенное поведение, потому что оно начинается с _
, за которым следует заглавная буква (вряд ли проблема в реальной жизни).
"#ifndef _UTILITIES_H
" - Плохое название для защиты жатки. Любое имя, начинающееся с подчеркивания и сопровождаемое заглавной буквой, - это зарезервировано для реализации, и вам не разрешается его использовать. Также; это вряд ли будет уникальным. См. stackoverflow.com/questions/49688518/…
Я поменял для этого защиту включения и получил следующие ошибки: g++ -g -Wall -c utilities.cpp utilities.cpp: In function ‘double calcNorm(std::vector<double>)’: utilities.cpp:5:8: error: redefinition of ‘double calcNorm(std::vector<double>)’ double calcNorm(std::vector <double> array) //template? ^ utilities.h:3:8: note: ‘double calcNorm(std::vector<double>)’ previously defined here #pragma once ^ makefile:12: recipe for target 'utilities.o' failed make: *** [utilities.o] Error 1
Я почти уверен, что это не полный MCVE. Порядок включения или плохое имя для защиты включения не может привести к ошибке, как сообщается.
@NathanOliver Я использую противоположное соглашение: сначала собственные заголовки. Как ни странно, мои рассуждения точно такие же, как и ваши.
И я также изменил имя защиты заголовка, и это тоже не устранило проблему.
Хм. Я думаю, что нашел проблему. У меня был этот двоичный файл с именем utilities.h.gch
(не знаю, откуда он) в каталоге. Я удалил его, и проблема с компилятором исчезла.
@ user2079303 и SergeyA. Думаю, я не должен был быть таким общим. В файле реализации для файла заголовка я делаю обратное тому, что я сказал, чтобы найти, есть ли в файле заголовка недостающие зависимости. В моем основном файле я делаю это так, как я сказал, поэтому мне не нужно беспокоиться о том, что мои включения заставят меня думать, что в стандартных включаемых файлах есть ошибка.
@SergeyA Полностью согласен. Я помещаю свой заголовок включает первый (если я не использую среду PCH, где, очевидно, сначала идет PCH, но я никогда не включаю это до конца проекта), затем заголовки библиотеки. Это гарантирует, что вы не создадите .h, который неявно зависит от других заголовков, извлеченных из включающего файла .cpp.
@NathanOliver я предпочитаю: сначала мои собственные заголовки, затем заголовки библиотеки, затем стандартные заголовки. Это гарантирует, что мои собственные файлы заголовков являются самодостаточными и не зависят от транзитивных включений.
«заголовки в квадратных скобках всегда должны быть перечислены первыми» - Нет.
Это не имеет ничего общего с порядком включений, а все связано с правилами поиска включений.
Оператор include, такой как #include "foo.h"
, заставит препроцессор сначала просмотреть текущий каталог исходного файла, тогда, независимо от того, какие пути включения вы указали (и пути по умолчанию). Оператор include, такой как #include <foo.h>
, будет делать то же самое, за исключением того, что он пропускает начальный шаг «просмотр текущего каталога».
Нет. Единственная разница между заголовками, заключенными в угловые скобки, и заголовками, заключенными в кавычки, заключается в том, где их будет искать система. (Обратите внимание, что стандартные заголовки могут вообще не существовать как отдельные файлы; это деталь реализации, о которой вам не нужно беспокоиться.)
When compiling a C++ source file, if you have multiple headers consisting of bracketed and quoted headers, do the bracketed headers have to always be listed first?
Нет.
Файлы заголовков (в исходных файлах, а также в других файлах заголовков) могут быть в любом порядке. Фактически, никакой файл заголовка даже не нужно включать в начало исходного файла, если каждый файл заголовка находится перед любыми объявлениями, которые зависят от объявлений из заголовка. В этом отношении не имеет значения, был ли путь указан с использованием угловых скобок или двойных кавычек.
Технически заголовок может быть написан таким образом, чтобы перед ним были включены другие заголовки, но в целом это неприятный запах кода и чаще всего ошибка.
Исключением является предварительно скомпилированный заголовок, который является нестандартной функцией, реализованной некоторыми компиляторами. Предварительно скомпилированный заголовок должен быть перед любым объявлением (и, следовательно, любым другим включением) исходного файла.
Hmmm. I think I found the issue. I had this binary file called utilities.h.gch (not sure where it came from) in the directory. I removed it and the compiler issue went away.
Ах. gch - это расширение файла, используемое предварительно скомпилированными заголовками, и это объясняет, почему вашего примера было недостаточно для воспроизведения вашей проблемы. Дополнительные сведения о том, как ведут себя предварительно скомпилированные заголовки, см. В руководстве к вашему компилятору.
Краткий ответ: Нет.
Длинный ответ:
Директива #include
практически буквально вставляет содержимое этого файла в исходный код. Заголовок должен быть включен до того, как будут использованы какие-либо его функции. Период. Если вы включите iostream
в конце, но не используете этот заголовок, проблем не будет.
Короткий ответ - нет'. Длинный ответ - MCVE, пожалуйста.