Stack at this Error: неопределенная ссылка на `Calcu & lt; int & gt; :: add (int, int) '[duplicate]

Как будто вы пытаетесь получить доступ к объекту, который является null. Рассмотрим ниже пример:

TypeA objA;

. В это время вы только что объявили этот объект, но не инициализировали или не инициализировали. И всякий раз, когда вы пытаетесь получить доступ к каким-либо свойствам или методам в нем, он будет генерировать NullPointerException, что имеет смысл.

См. Также этот пример:

String a = null;
System.out.println(a.toString()); // NullPointerException will be thrown
1391
задан Aaron McDaid 24 February 2015 в 21:54
поделиться

14 ответов

not необходимо поместить реализацию в файл заголовка, см. альтернативное решение в конце этого ответа.

В любом случае причина, по которой ваш код не работает, что при создании экземпляра шаблона компилятор создает новый класс с заданным аргументом шаблона. Например:

template<typename T>
struct Foo
{
    T bar;
    void doSomething(T param) {/* do stuff using T */}
};

// somewhere in a .cpp
Foo<int> f; 

При чтении этой строки компилятор создаст новый класс (назовем его FooInt), что эквивалентно следующему:

struct FooInt
{
    int bar;
    void doSomething(int param) {/* do stuff using int */}
}

Следовательно, компилятор должен иметь доступ к реализации методов, чтобы создать экземпляр с аргументом шаблона (в данном случае int). Если эти реализации не были в заголовке, они не были бы доступны, поэтому компилятор не смог бы создать экземпляр шаблона.

Общим решением для этого является запись объявления шаблона в заголовок файла, затем реализовать класс в файле реализации (например, .tpp) и включить этот файл реализации в конец заголовка.

// Foo.h
template <typename T>
struct Foo
{
    void doSomething(T param);
};

#include "Foo.tpp"

// Foo.tpp
template <typename T>
void Foo<T>::doSomething(T param)
{
    //implementation
}

Таким образом, реализация по-прежнему отделена от объявления, но доступен компилятору.

Другое решение состоит в том, чтобы сохранить реализацию отдельно и явно создать все экземпляры шаблона, которые вам понадобятся:

// Foo.h

// no implementation
template <typename T> struct Foo { ... };

//----------------------------------------    
// Foo.cpp

// implementation of Foo's methods

// explicit instantiations
template class Foo<int>;
template class Foo<float>;
// You will only be able to use Foo with int or float

Если мое объяснение isn ' t достаточно ясно, вы можете взглянуть на C ++ Super-FAQ по этому вопросу .

1211
ответ дан milleniumbug 17 August 2018 в 17:39
поделиться
  • 1
    Фактически явное создание экземпляра должно быть в .cpp-файле, который имеет доступ к определениям для всех функций-членов Foo, а не в заголовке. – Mankarse 28 May 2011 в 11:21
  • 2
    «компилятор должен иметь доступ к реализации методов, чтобы создать экземпляр с аргументом шаблона (в этом случае int). Если бы эти реализации не были в заголовке, они не были бы доступны " Но почему реализация в файле .cpp недоступна компилятору? Компилятор также может получить доступ к .cpp-информации, как иначе это превратит их в .obj-файлы? EDIT: ответ на этот вопрос находится в ссылке, приведенной в этом ответе ... – xcrypt 15 January 2012 в 01:56
  • 3
    Я не думаю, что это объясняет тот вопрос, который ясно, что ключевое значение, очевидно, связано с компиляционным блоком, который не упоминается в этом сообщении – zinking 23 August 2012 в 10:47
  • 4
    @Gabson: структуры и классы эквивалентны, за исключением того, что модификатор доступа по умолчанию для классов является «частным», тогда как он является открытым для структур. Есть еще несколько небольших различий, которые вы можете узнать, посмотрев на этот вопрос . – Luc Touraille 21 February 2014 в 16:12
  • 5
    Используйте явный экземпляр, иначе вы скоро получите очень медленные сборки. – Nils 14 July 2014 в 14:43

Это связано с требованием отдельной компиляции и потому, что шаблоны являются полиморфизмом типа создания экземпляров.

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

  • foo.h объявляет интерфейс class MyClass<T>
  • foo.cpp определяет реализацию class MyClass<T>
  • bar.cpp использует MyClass<int>

. Отдельное средство компиляции. Я должен скомпилировать foo.cpp независимо от bar.cpp. Компилятор полностью выполняет всю сложную работу по анализу, оптимизации и генерации кода на каждом модуле компиляции; нам не нужно анализировать целую программу. Только компоновщик должен обрабатывать всю программу одновременно, и задача компоновщика значительно упрощается.

bar.cpp даже не нужно существовать при компиляции foo.cpp, но я все равно должен быть в состоянии связать foo.o Я уже имел вместе с bar.o Я только что выпустил, не перекомпилируя foo.cpp. foo.cpp может даже быть скомпилирован в динамическую библиотеку, распределенную где-то в другом месте без foo.cpp, и связан с кодом, который они пишут спустя годы после того, как я написал foo.cpp.

«Полиморфизм в стиле объектов» означает, что template MyClass<T> не является общим классом, который может быть скомпилирован в код, который может работать для любого значения T. Это добавит накладные расходы, такие как бокс, необходимо передать указатели на функции для распределителей и конструкторов и т. Д. Намерение шаблонов C ++ состоит в том, чтобы избежать необходимости писать почти идентичные class MyClass_int, class MyClass_float и т. Д., Но все же быть в состоянии закончить с компилируемым кодом, который в основном выглядит так, как если бы мы имели каждую версию отдельно. Таким образом, шаблон является буквально шаблоном; шаблон класса не класс, это рецепт создания нового класса для каждого T, с которым мы сталкиваемся. Шаблон не может быть скомпилирован в код, только результат создания экземпляра шаблона может быть скомпилирован.

Итак, когда foo.cpp скомпилирован, компилятор не может видеть bar.cpp, чтобы знать, что MyClass<int> необходимо. Он может видеть шаблон MyClass<T>, но он не может испускать код для этого (это шаблон, а не класс). И когда компилируется bar.cpp, компилятор может видеть, что ему нужно создать MyClass<int>, но он не может видеть шаблон MyClass<T> (только его интерфейс в foo.h), поэтому он не может его создать.

Если foo.cpp сам использует MyClass<int>, тогда код для него будет сгенерирован при компиляции foo.cpp, поэтому, когда bar.o связан с foo.o, они могут быть подключены и будут работать. Мы можем использовать этот факт, чтобы позволить конечный набор экземпляров шаблонов быть реализован в .cpp-файле, написав один шаблон. Но bar.cpp не может использовать шаблон в качестве шаблона и создавать его на всех типах, которые ему нравятся; он может использовать только ранее существовавшие версии шаблона, которые автор foo.cpp думал предоставить.

Вы можете подумать, что при компиляции шаблона компилятор должен «сгенерировать все версии», с теми, которые никогда не используются, отфильтровываются во время связывания. Помимо огромных накладных расходов и экстремальных трудностей, с которыми сталкивался такой подход, поскольку «модификаторы типа», такие как указатели и массивы, позволяют даже встроенным типам создавать бесконечное количество типов, что происходит, когда я расширяю свою программу добавив:

  • baz.cpp объявляет и реализует class BazPrivate и использует MyClass<BazPrivate>

Невозможно, чтобы это могло работать, если мы либо

  1. Необходимо перекомпилировать foo.cpp каждый раз, когда мы меняем любой другой файл в программе , в случае, если он добавил новый романный экземпляр MyClass<T>
  2. Требовать, чтобы baz.cpp содержал (возможно, через заголовок) полный шаблон MyClass<T>, чтобы компилятор мог генерировать MyClass<BazPrivate> во время компиляции baz.cpp.

Никто не любит (1), потому что системы компиляции целых программ принимают forever для компиляции и потому что это делает невозможным распространение компилированных библиотек без исходного кода. Итак, у нас есть (2).

174
ответ дан Ben 17 August 2018 в 17:39
поделиться
  • 1
    подчеркнутая цитата , шаблон является буквально шаблоном; шаблон класса не является классом, это рецепт создания нового класса для каждого T, с которым мы сталкиваемся – v.oddou 25 April 2016 в 09:57
  • 2
    Я хотел бы знать, возможно ли выполнять явные экземпляры где-то, кроме заголовка или исходного файла класса? Например, выполните их в main.cpp? – gromit190 9 March 2017 в 09:01
  • 3
    @Birger Вы должны иметь возможность сделать это из любого файла, который имеет доступ к полной реализации шаблона (либо потому, что он находится в том же файле, либо через заголовок). – Ben 9 March 2017 в 12:02
  • 4
    Как это ответ? Он не предлагает решения, кроме риторики. – ajeh 2 April 2018 в 18:48
  • 5
    @ajeh Это не риторика. Вопрос заключается в том, почему вам нужно внедрять шаблоны в заголовок? & Quot ;, поэтому я объяснил технические варианты, которые делает язык C ++, который приводит к этому требованию. Прежде чем я написал свой ответ, другие уже предоставили обходные пути, которые не являются полными решениями, потому что не может быть полным решением. Я чувствовал, что эти ответы будут дополнены более полным обсуждением «почему». угол вопроса. – Ben 2 April 2018 в 22:02

На самом деле, версии стандарта C ++ до того, как C ++ 11 определили ключевое слово «export», [] , можно просто объявить шаблоны в файле заголовка и реализовать их в другом месте.

К сожалению, ни один из популярных компиляторов не реализовал это ключевое слово. Единственный, о котором я знаю, - это интерфейс, написанный Edison Design Group, который используется компилятором Comeau C ++. Все остальные настаивали на том, что вы пишете шаблоны в заголовочных файлах, нуждающихся в определении кода для надлежащего создания экземпляра (как уже указывали другие).

В результате стандартная комиссия ISO C ++ решила удалить export особенность шаблонов, начинающихся с C ++ 11.

48
ответ дан einpoklum 17 August 2018 в 17:39
поделиться
  • 1
    В результате экспортный шаблон был удален из C ++ 11. – johannes 23 October 2011 в 01:47
  • 2
    @johannes: Я не понял этого, спасибо. Фактически, вы все равно не могли бы использовать export, так как это могло бы связать вас с компилятором Comeau. Это была «мертвая функция»; только один, которого я бы любил видеть, реализован повсеместно. – DevSolar 23 October 2011 в 07:06
  • 3
    ... и через пару лет, я , наконец, понял, что export действительно имеет данный нас, а что нет ... и теперь я полностью согласен с Люди EDG: Это не привело бы нас к тому, что большинство людей (включая меня в 11-м классе) думают, что это будет, а стандарт C ++ лучше без него. – DevSolar 19 November 2015 в 11:27
  • 4
    @DevSolar: эта статья является политической, повторяющейся и плохо написанной. это обычная стандартная проза. Неизменно длинный и скучный, говоря в основном 3 раза то же самое по десяткам страниц. Но теперь мне сообщили, что экспорт не является экспортом. Это хороший интеллект! – v.oddou 25 April 2016 в 09:51
  • 5
    @ v.oddou: Хороший разработчик и хороший технический писатель - это два отдельных набора навыков. Некоторые могут делать то и другое, многие не могут. ;-) – DevSolar 25 April 2016 в 09:58

Если проблема заключается в дополнительном времени компиляции и раздувании бинарного размера, полученном путем компиляции .h как части всех используемых им .cpp-модулей, во многих случаях то, что вы можете сделать, - это сделать класс шаблона опустившимся из не-шаблонного базовый класс для не зависящих от типа частей интерфейса, и что базовый класс может иметь свою реализацию в файле .cpp.

6
ответ дан Eric Shaw 17 August 2018 в 17:39
поделиться
  • 1
    Этот ответ должен быть изменен еще больше. I & quot; независимо & quot; обнаружил тот же подход и специально искал кого-то еще, чтобы использовать его уже, так как мне любопытно, является ли это официальным шаблоном и есть ли у него имя. Мой подход заключается в реализации class XBase везде, где мне нужно реализовать template class X, поместив зависящие от типа части в X и все остальные в XBase. – Fabio A. 4 November 2016 в 09:38

Это означает, что наиболее переносимым способом определения реализации методов классов шаблонов является определение их внутри определения класса шаблона.

template < typename ... >
class MyClass
{

    int myMethod()
    {
       // Not just declaration. Add method implementation here
    }
};
13
ответ дан Evan Teran 17 August 2018 в 17:39
поделиться

Это точно правильно, потому что компилятор должен знать, какой тип он предназначен для распределения. Поэтому классы шаблонов, функции, перечисления и т. Д. Должны быть реализованы также в файле заголовка, если он должен быть опубликован или частично из библиотеки (статический или динамический), поскольку файлы заголовков НЕ скомпилированы в отличие от файлов c / cpp, которые находятся. Если компилятор не знает, что тип не может его скомпилировать. В .Net это возможно, потому что все объекты происходят из класса Object. Это не .Net.

6
ответ дан Flexo 17 August 2018 в 17:39
поделиться
  • 1
    & quot; заголовочные файлы НЕ компилируются & quot; - Это действительно странный способ описать это. Заголовочные файлы могут быть частью единицы перевода, как и «c / cpp». файл. – Flexo♦ 17 September 2011 в 13:26
  • 2
    Фактически, это почти противоположность истине, которая заключается в том, что заголовочные файлы очень часто компилируются много раз, тогда как исходный файл обычно компилируется один раз. – xaxxon 21 December 2015 в 23:46

Шаблоны должны использоваться в заголовках, потому что компилятор должен создавать экземпляры разных версий кода в зависимости от параметров, заданных / выведенных для параметров шаблона. Помните, что шаблон не представляет собой код напрямую, а шаблон для нескольких версий этого кода. Когда вы компилируете функцию non-template в файле .cpp, вы компилируете конкретную функцию / класс. Это не относится к шаблонам, которые могут быть созданы с использованием разных типов, а именно, если при замене параметров шаблона конкретными типами необходимо исправить конкретный код.

Была функция с ключевым словом export, которая была предназначенный для отдельной компиляции. Функция export устарела в C++11 и, AFAIK, только один компилятор реализовал ее. Вы не должны использовать export. Отдельная компиляция невозможна в C++ или C++11, но, возможно, в C++17, если понятия в нее входят, мы могли бы иметь некоторый способ отдельной компиляции.

Для отдельной компиляции, которая должна быть достигнута, разделить проверка шаблона тела должна быть возможна. Кажется, что решение возможно с концепциями. Взгляните на этот документ , недавно представленный на совещании по стандартам. Я думаю, что это не единственное требование, поскольку вам все равно необходимо создать код кода шаблона в коде пользователя.

Отдельная проблема компиляции для шаблонов, я думаю, это также проблема, возникающая при миграции на модули, которые в настоящее время работают.

26
ответ дан Germán Diago 17 August 2018 в 17:39
поделиться

Здесь много правильных ответов, но я хотел добавить это (для полноты):

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

Изменить: добавление примера явного создания экземпляра шаблона. Используется после того, как шаблон определен, и определены все функции-члены.

template class vector<int>;

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

Вышеприведенный пример бесполезен, поскольку вектор полностью определен в заголовках, за исключением случаев, когда common include file (precompiled header?) использует extern template class vector<int>, чтобы не создавать его из всех других (1000?) файлов, которые используют вектор.

200
ответ дан Jonathan Wakely 17 August 2018 в 17:39
поделиться
  • 1
    Тьфу. Хороший ответ, но нет реального чистого решения. Выделение всех возможных типов шаблона не похоже на то, что должен быть шаблон. – Jiminion 17 July 2014 в 18:49
  • 2
    Это во многих случаях может быть хорошим, но обычно нарушает назначение шаблона, который предназначен для того, чтобы вы могли использовать класс с любым type без их ручного перечисления. – Tomáš Zato 9 December 2014 в 04:27
  • 3
    vector не является хорошим примером, поскольку контейнер по своей сути нацеливается на «все». типы. Но очень часто вы создаете шаблоны, предназначенные только для определенного набора типов, например, числовые типы: int8_t, int16_t, int32_t, uint8_t, uint16_t и т. Д. В этом случае имеет смысл использовать шаблон , но явным образом их создание для всего набора типов также возможно и, на мой взгляд, рекомендуется. – UncleZeiv 3 June 2015 в 15:41
  • 4
    Используемый после того, как шаблон определен, «и все функции-члены определены». Благодаря ! – Vitt Volt 16 February 2017 в 07:04
  • 5
    Я хотел бы знать, возможно ли выполнять явные экземпляры где-то, кроме заголовка или исходного файла класса? Например, выполните их в main.cpp? – gromit190 9 March 2017 в 09:01

Хотя стандарт C ++ не имеет такого требования, некоторым компиляторам требуется, чтобы все шаблоны функций и классов были доступны в каждой используемой системе переводов. Фактически для этих компиляторов тела шаблонных функций должны быть доступны в файле заголовка. Повторить: это означает, что эти компиляторы не позволят их определять в файлах без заголовка, таких как .cpp-файлы

Существует ключевое слово export , которое должно смягчать этот проблема, но он нигде не близок к тому, чтобы быть портативным.

31
ответ дан Jonny Henly 17 August 2018 в 17:39
поделиться
  • 1
    Почему я не могу реализовать их в .cpp-файле с ключевым словом «inline» ?? – MainID 30 January 2009 в 11:20
  • 2
    Вы можете, и вам не нужно указывать «встроенный». даже. Но вы сможете использовать их только в этом файле cpp и нигде больше. – vava 30 January 2009 в 11:28
  • 3
    Это почти самый точный ответ, кроме & quot ;, это означает, что эти компиляторы не позволят им быть определены в файлах без заголовка, таких как .cpp-файлы & quot; явно ложно. – Lightness Races in Orbit 14 August 2011 в 18:59

При компиляции шаблоны должны быть созданы экземплярами , прежде чем их компилировать в объектный код. Это создание может быть достигнуто только в том случае, если известны аргументы шаблона. Теперь представьте сценарий, в котором функция шаблона объявлена ​​в a.h, определенная в a.cpp и используемая в b.cpp. Когда компилируется a.cpp, не обязательно известно, что для предстоящей компиляции b.cpp потребуется экземпляр шаблона, не говоря уже о том, какой конкретный экземпляр это будет.

Можно утверждать, что компиляторы можно сделать умнее, чтобы «смотреть вперед» для всех применений шаблона, но я уверен, что это было бы нелегко создавать рекурсивные или другие сложные сценарии. AFAIK, компиляторы этого не делают. Как заметил Антон, некоторые компиляторы поддерживают явные декларации экспорта экземпляров шаблонов, но не все компиляторы поддерживают его (пока?).

64
ответ дан K DawG 17 August 2018 в 17:39
поделиться
  • 1
    & Quot; экспорт & Quot; является стандартным, но его просто сложно реализовать, поэтому большинство команд компилятора еще не сделали этого. – vava 30 January 2009 в 11:27
  • 2
    экспорт не устраняет необходимость раскрытия источника, а также не снижает зависимость от компиляции, в то время как для этого требуются огромные усилия со стороны разработчиков компиляторов. Поэтому Херб Саттер сам попросил строителей компилятора «забыть» об экспорте. Поскольку требуемое время было бы лучше потратить в другом месте ... – Pieter 30 January 2009 в 16:13
  • 3
    Поэтому я не думаю, что экспорт еще не реализован. Вероятно, это никогда не будет сделано кем-то еще, кроме EDG, после того, как другие увидели, сколько времени прошло, и как мало было получено – Pieter 30 January 2009 в 16:14
  • 4
    Если это вас интересует, бумага называется «Почему мы не можем позволить себе экспорт», она указана в его блоге ( gotw.ca/publications ), но там нет pdf (быстрый google должен повернуть его хотя) – Pieter 30 January 2009 в 16:22
  • 5
    Хорошо, спасибо за хороший пример и объяснение. Вот мой вопрос: почему компилятор не может понять, где вызывается шаблон, и скомпилировать эти файлы перед компиляцией файла определения? Я могу представить, что это можно сделать в простом случае ... Является ли ответ, что взаимозависимости испортит заказ довольно быстро? – Vlad 11 October 2013 в 18:54

Хотя есть много хороших объяснений выше, я пропускаю практический способ разделения шаблонов на заголовок и тело. Моя главная задача - избегать перекомпиляции всех пользователей шаблонов, когда я меняю свое определение. Все экземпляры шаблонов в корпусе шаблона не являются жизнеспособным решением для меня, поскольку автор шаблона может не знать всех, если его использование и пользователь шаблона могут не иметь права его модифицировать. Я принял следующий подход, который также работает и для более старых компиляторов (gcc 4.3.4, aCC A.03.13).

Для каждого использования шаблона в его собственном файле заголовка (сгенерированном из модели UML) имеется typedef, , Его тело содержит экземпляр (который заканчивается в библиотеке, которая связана в конце). Каждый пользователь шаблона включает этот файл заголовка и использует typedef.

Схематический пример:

MyTemplate.h:

#ifndef MyTemplate_h
#define MyTemplate_h 1

template <class T>
class MyTemplate
{
public:
  MyTemplate(const T& rt);
  void dump();
  T t;
};

#endif

MyTemplate.cpp:

#include "MyTemplate.h"
#include <iostream>

template <class T>
MyTemplate<T>::MyTemplate(const T& rt)
: t(rt)
{
}

template <class T>
void MyTemplate<T>::dump()
{
  cerr << t << endl;
}

MyInstantiatedTemplate.h:

#ifndef MyInstantiatedTemplate_h
#define MyInstantiatedTemplate_h 1
#include "MyTemplate.h"

typedef MyTemplate< int > MyInstantiatedTemplate;

#endif

MyInstantiatedTemplate.cpp:

#include "MyTemplate.cpp"

template class MyTemplate< int >;

main.cpp:

#include "MyInstantiatedTemplate.h"

int main()
{
  MyInstantiatedTemplate m(100);
  m.dump();
  return 0;
}

Таким образом, нужно будет перекомпилировать только экземпляры шаблонов, не всех пользователей шаблонов (и зависимостей).

9
ответ дан lafrecciablu 17 August 2018 в 17:39
поделиться

Компилятор будет генерировать код для каждого экземпляра шаблона при использовании шаблона во время этапа компиляции. В процессе компиляции и компоновки файлы .cpp преобразуются в чистый объектный или машинный код, который в них содержит ссылки или неопределенные символы, поскольку файлы .h, которые включены в ваш main.cpp, не имеют реализации YET. Они готовы быть связаны с другим объектным файлом, который определяет реализацию для вашего шаблона, и, следовательно, у вас есть полный исполняемый файл a.out. Однако, поскольку шаблоны необходимо обрабатывать на этапе компиляции, чтобы сгенерировать код для каждого экземпляра шаблона, который вы делаете в своей основной программе, ссылка не поможет, поскольку компиляция main.cpp в main.o, а затем компиляция вашего шаблона .cpp в template.o, а затем ссылка не будет достигать цели шаблонов, потому что я связываю различные экземпляры шаблонов с одной и той же реализацией шаблона! И шаблоны должны делать обратное, т. Е. Иметь одну реализацию, но допускать много доступных экземпляров посредством использования одного класса.

Значение typename T get заменяется во время этапа компиляции, а не на этапе связывания, поэтому, если я попытаюсь для компиляции шаблона без замены T в качестве конкретного типа значения, чтобы он не работал, потому что это определение шаблонов - это процесс времени компиляции, а мета-программирование btw - все об использовании этого определения.

1
ответ дан Moshe Rabaev 17 August 2018 в 17:39
поделиться

Просто добавьте что-то примечательное здесь.


myQueue.hpp:

template <class T> 
class QueueA {
    int size;
    ...
public:
    template <class T> T dequeue() {
       // implementation here
    }

    bool isEmpty();

    ...
}    

myQueue можно определить методы шаблонного класса, которые просто прекрасны в файле реализации. cpp:

// implementation of regular methods goes like this:
template <class T> bool QueueA<T>::isEmpty() {
    return this->size == 0;
}


main()
{
    QueueA<char> Q;

    ...
}
0
ответ дан Nik-Lz 17 August 2018 в 17:39
поделиться

Способ иметь отдельную реализацию выглядит следующим образом.

//inner_foo.h

template <typename T>
struct Foo
{
    void doSomething(T param);
};


//foo.tpp
#include "inner_foo.h"
template <typename T>
void Foo<T>::doSomething(T param)
{
    //implementation
}


//foo.h
#include <foo.tpp>

//main.cpp
#include <foo.h>

inner_foo имеет форвардные объявления. foo.tpp имеет реализацию и включает inner_foo.h; и foo.h будет иметь только одну строку, чтобы включить foo.tpp.

Во время компиляции содержимое foo.h копируется в foo.tpp, а затем весь файл копируется в foo.h после который он компилирует. Таким образом, ограничений нет, и именование согласовано в обмен на один дополнительный файл.

Я делаю это, потому что статические анализаторы для кода разбиваются, когда он не видит передовые объявления класса в * .tpp. Это раздражает при написании кода в любой среде IDE или с помощью YouCompleteMe или других.

2
ответ дан Pranay 17 August 2018 в 17:39
поделиться
Другие вопросы по тегам:

Похожие вопросы: