Почему функции класса шаблона должны быть объявлены в том же модуле перевода? - PullRequest
4 голосов
/ 06 ноября 2011

Возьмите этот код, например:

/*
 * foo.h
 *
 *  Created on: Nov 5, 2011
 *      Author: AutoBotAM
 */

#ifndef FOO_H_
#define FOO_H_

template<typename Type>
class Foo
{
public:
    void Bar(Type object);
};


#endif /* FOO_H_ */

.

/*
 * foo.cpp
 *
 *  Created on: Nov 5, 2011
 *      Author: AutoBotAM
 */

#include <iostream>
using namespace std;

#include "foo.h"

template<typename Type>
void Foo<Type>::Bar(Type object)
{
    cout << object;
}

.

/*
 * main.cpp
 *
 *  Created on: Oct 15, 2011
 *      Author: AutoBotAM
 */

#include <iostream>
using namespace std;

#include "foo.h"

Foo<int> foo;

int main()
{
    cout << "The year is ";
    foo.Bar(2011);
    return 0;
}

Так я обычно объявляю не шаблонклассы.К сожалению, этот код приводит к ошибке ../src/main.cpp:18: undefined reference to 'Foo<int>::Bar(int)' (в MinGW).Я кое-что прочитал, и оказалось, что вы должны объявить классы шаблонов в одном и том же модуле перевода, например:

/*
 * foo.h
 *
 *  Created on: Nov 5, 2011
 *      Author: AutoBotAM
 */

#ifndef FOO_H_
#define FOO_H_

template<typename Type>
class Foo
{
public:
    void Bar(Type object)
    {
        cout << object;
    }
};


#endif /* FOO_H_ */

Мой большой вопрос: зачем вам это делать?Я мог бы представить несколько подводных камней в разработке с этой схемой.Например, представьте, что у нас было 50 единиц перевода #inclusive foo.h, и мы изменили значение на void Foo::Bar(Type).Поскольку Bar находится в заголовочном файле, нам нужно дождаться компиляции всех 50 единиц перевода, прежде чем мы получим какие-либо результаты.Если бы у нас Bar работал в foo.cpp по отдельности, нам нужно было бы подождать только 1 единицу перевода для компиляции.Есть ли способы решить эту проблему?

Спасибо за любой совет!

Ответы [ 4 ]

4 голосов
/ 06 ноября 2011

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

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

template class Foo<int>;

, теперь вы можете использовать Foo<int> s где угодно, даже когда реализации находятся в собственной единице перевода.Однако вы не можете использовать любой другой тип Foo<>, так как компоновщик не сможет найти свои функции (на самом деле они не существуют).

3 голосов
/ 06 ноября 2011

Шаблоны не являются типами. Это только шаблоны. Они становятся типом только тогда, когда они созданы (с полным набором параметров).

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

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

С другой стороны, рассмотрим следующую настройку:

// Header file:
template <typename T> struct Foo { void f(); }

// "Implementation" file
template <typename T> void Foo::f() { /* stuff */ }

Если вы скомпилируете файл реализации, он не содержит никакого кода , так как нет шаблона экземпляр для компиляции. Пользователь файла заголовка может создать экземпляр Foo<int>, но тело класса никогда не будет создано в любом TU, поэтому вы получаете ошибку компоновщика при сборке программы.

Это может помочь думать о шаблонах как о инструменте генерации кода , а не как о реальном коде, по крайней мере для целей компиляции.

0 голосов
/ 06 ноября 2011

Большинство компиляторов пока не поддерживают внешние шаблоны, что позволило бы разделить тип cpp / h, который вы ищете. Однако вы все равно можете отделить объявления шаблона от реализаций, аналогичных тем, которые вы хотите. Поместите объявления в файлы .h, поместите реализации в отдельный исходный файл с любым желаемым расширением (популярны .i и .ipp), а затем #include исходный файл внизу файла .h. Компилятор видит один блок перевода, и вы получаете разделение кода.

/*
* foo.h
*
* Created on: Nov 5, 2011
* Author: AutoBotAM
*/
#ifndef FOO_H_
#define FOO_H_

template<typename Type>
class Foo
{
public:
    void Bar(Type object);
};

#include "foo.ipp"

#endif /* FOO_H_ */

.

/*
* foo.ipp
*
* Created on: Nov 5, 2011
* Author: AutoBotAM
*/

#include <iostream>

template<typename Type>
void Foo<Type>::Bar(Type object)
{
    std::cout << object;
}
0 голосов
/ 06 ноября 2011

Шаблоны метапрограммирование . Они не компилируются (напрямую) в объектный код. Только результаты их.

...