Требует ли определенного порядка для #include в C++ признак плохого дизайна библиотеки / заголовка?

Я использовал несколько очень масштабных систем и никогда не видел требуемого заказа, но недавно столкнулся с ним. Есть ли в библиотеке STL или STD или даже в Boost какие-либо случаи, когда определенные включения должны приходить в определенном порядке?

13 ответов

Решение

Есть ли в библиотеке STL или STD или даже в Boost какие-либо случаи, когда определенные включения должны приходить в определенном порядке?

Для стандарта ответ решительно, нет. Я полагаю, что то же самое верно для Boost, хотя я не смотрел это.

Из стандарта С:

Стандартные заголовки могут быть включены в любом порядке; каждый из них может быть включен более одного раза в данную область, без эффекта отличающегося от включения только один раз, за ​​исключением того, что эффект включения <assert.h> зависит от определения NDEBUG (см. 7.2).

Стандарт C++ имеет аналогичную формулировку.

Я предпочитаю, чтобы заголовки включали свои собственные зависимости, но я работал с людьми, которые считают это "расточительным". По моему мнению, отсутствие заголовков, включающих их зависимости, - бесполезная ранняя оптимизация.

Это определенно звучит как плохой дизайн. Если каким-то образом требуется определенный порядок, библиотека должна предоставить один заголовок, который включает другие заголовки в правильном порядке.

Что касается буста и STL, я почти уверен, что еще не сталкивался с такой ситуацией.

Необходимость указать включает в конкретном порядке почти всегда указывается проблема дизайна. Одним из способов уменьшить вероятность непреднамеренного выполнения этого является использование практики включения заголовочного файла класса в качестве первого #include в файле реализации.

// A.cpp
#include "A.h"
#include "boost/shared_ptr.hpp"
#include <vector>

class A {
// ...
};

Таким образом, если, например, Ah использует вектор без правильного #include, A.cpp не будет компилироваться.

Я не могу вспомнить, где я поднял это; это могло быть из "Large Scale C++ Design" Лакоса (отличная книга, которая действительно могла бы использовать обновление).

Общепринятым методом является включение заголовка совместимости на уровне проекта (скажем, compat.h) в качестве первого заголовка любых исходных файлов.c /.cpp, который определяет набор необходимых макросов, таких как __STDC_LIMIT_MACROS, __REENTRANT и другие макросы всего проекта, чтобы повлиять на последующее поведение стандартных заголовков.

Впервые я увидел это использование компетентным программистом для внутренней библиотеки. Позже я увидел, что проект 'git' (печально известные dvcs) также использовал эту технику.

Это "плохая вещь". Лучший способ был упомянут; но я уточню.

//a.h
#ifndef _A_H_
#define _A_H_

//... code ...

#endif
// -----------------
//b.h
#ifndef _B_H_
#define _B_H_
#include a.h

//... code ...

#endif
// -----------------
//main.cpp Try 1
#include "b.h" //<- okay!  b includes a, then does b
// -----------------
//main.cpp Try 2
#include "a.h" //<- includes a
#include "b.h" //<- okay!  b includes a, but skips redefining it, then does b
// -----------------
//main.cpp Try 3
#include "b.h" //<- b includes a, then does b
#include "a.h" //<- okay!  a skips redefining itself!
// -----------------
//main.cpp Try 4
#include "a.h" //<- fail!  b is not included anywhere =(

Есть ли в библиотеке STL или STD или даже в Boost какие-либо случаи, когда определенные включения должны приходить в определенном порядке?

Я никогда не сталкивался с этим, и если это так, то авторы должны быть уведомлены как можно скорее. И да, это очень плохой дизайн.

Если функции и / или классы, содержащиеся в заголовке (скажем, Ah), зависят от функций и / или классов, определенных в другом заголовке (скажем, Bh), я предпочитаю включать последний в первый, а не заставлять пользователей первого один, чтобы включить оба в определенном порядке.

Да:

// A.h
#pragma once
// or the #ifndef trick
#include "B.h"

// A.cpp
#include "A.h"

Нет:

// A.h
#pragma once
// or the #ifndef trick
//#include "B.h"

// A.cpp
#include "B.h"
#include "A.h"

Для меня это плохой дизайн, который, к сожалению, случается в Win32 API с включенным socket/socket2, если я правильно помню. Результатом является то, что ошибка в порядке включения вызовет набор ошибок, которые просто происходят из ниоткуда, и их может быть сложно отладить в тех случаях, когда зависимость меняет определения, но код по-прежнему компилируется.

В любом другом случае вы все равно столкнетесь с неприятностями. Если вы не включаете заголовок xh, потому что yh уже включает его, то ваш код зависит от зависимости yh от xh. Если позднее yh подвергается рефакторингу и больше не требует yh, удаление включения нарушит вашу базу кода. Это признак связывания (даже если не на уровне класса): изменения в одной части кодовой базы должны распространяться и распространяться на другие части кода.

Возможно, это признак того, что вы используете MFC, что, в свою очередь, может указывать на плохой дизайн (шутка... или это?)

(По крайней мере, в последний раз, когда я смотрел на MFC, было очень требовательно <windows.h>)

Мне нравится включать заголовки в алфавитном порядке - позволяет легко увидеть, что я уже сделал.

Если библиотека не работает, потому что она в неправильном порядке, то она сломана и должна быть исправлена, чтобы быть независимой от порядка.

Вы должны использовать include guard и forward-декларации, таким образом у вас не должно быть особых проблем с порядком включения заголовков.

Иногда все еще требуется, чтобы заголовок был первым или последним, не знаю почему.
(Например: в исходном SDK)

Да, требование определенного порядка для включений в C++ является признаком плохого дизайна библиотеки / заголовка.

Хотя для прямого использования может потребоваться включение более одного файла, чтобы полностью использовать класс. Смотрите пример ниже:

// Ах

class B; // forward declaration

class A
{
    void doStuff(const B& b);
};

// main.cpp

#include <A.h>
#include <B.h>

int main()
{
    A a;
    B b;
    a.doStuff(b);
}

Не в моих знаниях. Это довольно плохая практика. Я недавно столкнулся с этим с заголовком Windows и некоторым странным интерфейсом в моем коде.

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