При разделении шаблонных классов C++ на .hpp/.cpp файлы - действительно ли это возможно?

Если Вы хотите сделать что-то усовершенствованное (и безопасный с точки зрения типов), Вы могли бы хотеть смотреть на это: http://www.ibm.com/developerworks/java/library/j-configint/index.html

89
задан Community 23 May 2017 в 12:10
поделиться

11 ответов

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

Чтобы узнать, почему, давайте посмотрим на процесс компиляции. Файлы заголовка никогда не скомпилированы. Они предварительно назначены. Предварительный код затем расположен в клубе с файлом CPP, который фактически скомпилирован. Теперь, если компилятор должен генерировать соответствующий макет памяти для объекта, необходимо знать тип данных класса шаблона.

На самом деле следует понимать, что класс шаблона не является классом вообще, кроме шаблона для класса Декларация и определение, которое генерируется компилятором во время компиляции после получения информации о типе данных из аргумента. До тех пор, пока макет памяти не может быть создан, инструкции по определению метода не могут быть сгенерированы. Помните, что первый аргумент метода класса - это оператор «Это». Все методы классов преобразуются в индивидуальные методы с названием Mangling и первым параметром, как объект, который он работает. «Этот» аргумент, который фактически рассказывает о размере объекта, который в состоянии укладывания шаблона недоступен для компилятора, если только пользователь не содержит объект с действительным аргументом типа. В этом случае, если вы поместите определения метода в отдельный файл CPP и пытаетесь компилировать его файл объекта не будет сгенерирован с информацией о классе. Компиляция не потерпит неудачу, он генерирует файл объекта, но он не будет генерировать какой-либо код для класса шаблона в объекте. Это причина, по которой линкер не может найти символы в объектных файлах, а сборки выполняется.

Теперь, какая альтернатива скрывает важные детали реализации? Как мы все знаем, что основной целью разделяющего интерфейса от реализации является скрытие деталей реализации в двоичной форме. Это то, где вы должны отделить структуры данных и алгоритмы. Ваш шаблон классов должен представлять только структуры данных, а не алгоритмы. Это позволяет скрывать более ценные детали реализации в отдельных библиотеках классов нетемпературов, классы внутри которых работают на классах шаблона или просто используют их для хранения данных. Класс шаблона фактически будет содержать меньше кода для назначения, Get и установки данных. Остальная часть работы будет выполняться классами алгоритма.

Я надеюсь, что эта дискуссия будет полезна.

142
ответ дан 24 November 2019 в 07:11
поделиться

Ключевое слово 'export' - это способ отделить реализацию шаблона от объявления шаблона. Это было введено в стандарт C ++ без существующей реализации. Со временем только пара компиляторов фактически реализовали это. Прочтите подробную информацию в статье Inform IT об экспорте

0
ответ дан 24 November 2019 в 07:11
поделиться

Другой вариант - сделать что-то вроде:

#ifndef _STACK_HPP
#define _STACK_HPP

template <typename Type>
class stack {
    public:
            stack();
            ~stack();
};

#include "stack.cpp"  // Note the include.  The inclusion
                      // of stack.h in stack.cpp must be 
                      // removed to avoid a circular include.

#endif

Мне не нравится это предложение с точки зрения стиля, но оно может вам подойти.

0
ответ дан 24 November 2019 в 07:11
поделиться

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

0
ответ дан 24 November 2019 в 07:11
поделиться

У вас должно быть все в файле hpp. Проблема в том, что классы на самом деле не создаются до тех пор, пока компилятор не увидит, что они необходимы для какого-то ДРУГОГО файла cpp, поэтому у него должен быть весь код, доступный для компиляции шаблонного класса в то время.

Одна вещь, которая Я стараюсь разделить свои шаблоны на общую, не шаблонную часть (которую можно разделить между cpp / hpp) и часть шаблона, зависящую от типа, которая наследует не шаблонный класс.

1
ответ дан 24 November 2019 в 07:11
поделиться

Только если вы #include "stack.cpp в конце stack.hpp . Я бы рекомендовал этот подход, только если реализация относительно велика, и если вы переименуете файл .cpp в другое расширение, как чтобы отличить его от обычного кода.

1
ответ дан 24 November 2019 в 07:11
поделиться

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

2
ответ дан 24 November 2019 в 07:11
поделиться

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

Также можно экспортировать их через DLL (!), Но получить правильный синтаксис довольно сложно (специфичные для MS комбинации __declspec (dllexport) и ключевого слова export).

Мы использовали это в библиотеке math / geom, которая использовала шаблоны double / float, но было довольно много кода. (В то время я искал его в Google, но сегодня у меня нет этого кода.)

2
ответ дан 24 November 2019 в 07:11
поделиться

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

Лучшее, что вы можете сделать, - это поместить реализации ваших функций в файл «.tcc» или «.tpp» и # включить файл .tcc в конец файла .hpp. Однако это просто косметика; это все равно, что реализовать все в файлах заголовков. Это просто цена, которую вы платите за использование шаблонов.

6
ответ дан 24 November 2019 в 07:11
поделиться

Это возможно, если вы знаете, какие экземпляры вам понадобятся.

Добавьте следующий код в конец stack.cpp, и он заработает:

template class stack<int>;

Все нешаблонные методы стека будут созданы, и этап связывания будет работать нормально.

85
ответ дан 24 November 2019 в 07:11
поделиться

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

Простой и естественный способ - поместить методы в файл заголовка. Но есть и другой способ.

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

new stack.cpp:

#include <iostream>
#include "stack.hpp"
template <typename Type> stack<Type>::stack() {
        std::cerr << "Hello, stack " << this << "!" << std::endl;
}
template <typename Type> stack<Type>::~stack() {
        std::cerr << "Goodbye, stack " << this << "." << std::endl;
}
static void DummyFunc() {
    static stack<int> stack_int;  // generates the constructor and destructor code
    // ... any other method invocations need to go here to produce the method code
}
2
ответ дан 24 November 2019 в 07:11
поделиться
Другие вопросы по тегам:

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