Форум программистов, компьютерный форум CyberForum.ru

Как правильно разделить файл в шаблонами? - C++

Восстановить пароль Регистрация
 
Рейтинг: Рейтинг темы: голосов - 32, средняя оценка - 4.66
alsav22
5282 / 4801 / 442
Регистрация: 04.06.2011
Сообщений: 13,587
22.09.2011, 05:22     Как правильно разделить файл в шаблонами? #1
Попробовал разделить один файл на два. В заголовочный "proba.h" поместил прототип шаблона и специализацию. В одном файле исходного кода оставил функцию main(), а во второй поместил определения шаблона и специализации.

Заголовочный файл "proba.h":
C++
1
2
3
template <class T>
T max5(T m[], int x);
template <> const char* max5(const char* m[], int x);
Первый файл:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
using namespace std;
#include "proba.h"
 
int main()
{
     int t[6] = {777, 34, 34, 66, 777, 1234};
         int l = max5(t, 6);
         cout << l << endl;
 
    double t2[4] = {7.98, 7.90, 3.676, 6.56};
    double l2 = max5(t2, 4);
        cout << l2 << endl;
        
        const char* uk[4] = {"sd","987654321", "qwertyu", "123456789"};
        const char* n = max5(uk, 4);
        cout << &n << "  " << n <<  endl;
        
        system ("pause");
    return 0;
}
Второй файл:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
using namespace std;
#include "proba.h"
 
template <> const char* max5<const char*>( const char* m[], int x)
{
    int n = 0;
        unsigned int p = strlen (m[0]);
        for (int i = 1; i < x; i++)
        if (p < strlen (m[i]))
        {
                p = strlen (m[i]);
                n = i;
        }
 
        return m[n];
}       
 template <class T>
 T max5(T m[], int x)
{
        T p = m[0];
        for (int i = 1; i < x; i++)
        { 
                if (p < m[i]) p = m[i];
        }               
        return p;       
}
При постоении выдаёт ошибки:
1>------ Построение начато: проект: 8-1, Конфигурация: Debug Win32 ------
1> 8-1.cpp
1> Создание кода...
1> Компиляция...
1> 8-2.cpp
1> Создание кода...
1>8-1.obj : error LNK2019: ссылка на неразрешенный внешний символ "double __cdecl max5<double>(double * const,int)" (??$max5@N@@YANQANH@Z) в функции _main
1>8-1.obj : error LNK2019: ссылка на неразрешенный внешний символ "int __cdecl max5<int>(int * const,int)" (??$max5@H@@YAHQAHH@Z) в функции _main
1>D:\MY C++Projects\8-4-5\Debug\8-1.exe : fatal error LNK1120: 2 неразрешенных внешних элементов
========== Построение: успешно: 0, с ошибками: 1, без изменений: 0, пропущено: 0 ==========

Если же определене шаблона и специализации тоже записать в заголовочный файл, а не во второй файл исходного кода, то всё проходит нормально. Но если создать второй файл исходного кода (с этим заголовочным файлом) и в нём использовать шаблон, то компоновщик выдаёт ошибку:fatal error LNK1169: обнаружен многократно определенный символ - один или более.
Это из за того, что определения шаблона и специализации присутствуют (через заголовочный файл) и в первом файле и во втором. Как правильно это всё делается? С функциями получается, а с шаблонами непонятно как правильно делать.
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
kazak
 Аватар для kazak
3029 / 2350 / 155
Регистрация: 11.03.2009
Сообщений: 5,401
22.09.2011, 05:31     Как правильно разделить файл в шаблонами? #2
Для шаблонов определение и реализация должны быть в одном файле.
CAHTEXHUK
Заблокирован
22.09.2011, 05:44     Как правильно разделить файл в шаблонами? #3
когда компилятор видит шаблонную функцию он должен знать для каких аргументов его инстанцировать. То есть если у тебя определение шаблона и его вызов находятся в разных файлах(.Cxx), то ничего не получится, компилер не инстанцирует шаблон, а просто создаст ссылку на определение, которую должен разрешить компоновщик, но так как ничего не инстанцированно, то линкер в свою очередь лажает.

В учебной программе можешь не париться и писать всё в хидере, слишком сильно это время компиляции не увеличит, а для больших проектов существуют предкомпильные заголовки. Ещё как вариант можно явно инстанцировать всё что нужно в конце файла реализации и при этом следить, чтобы не проинтанцировать чтото дважды для одних и тех же параметров, иначе линкер опять обругается
alsav22
5282 / 4801 / 442
Регистрация: 04.06.2011
Сообщений: 13,587
22.09.2011, 07:38  [ТС]     Как правильно разделить файл в шаблонами? #4
Я в этих определениях уже запутался: прототип, объявление, определение, реализация... Ты ещё добавил инстанцировать (инстанация?). Если я правильно понимаю, то это означает создать экземпляр функции по шаблону? Писать всё в хидере не годится. Вернее, годится, если файл исходного кода один, а если не один и во втором тоже вызывается шаблонная функция, то компоновщик выдаёт двойное определение: fatal error LNK1169: обнаружен многократно определенный символ - один или более. Получается так, что если имеется несколько файлов исходного кода, в которых есть вызов шаблонной функции, то прототип шаблона должен присутствовать в каждом файле, а реализация шаблона только в одном. У меня так получилось: в одном заголовочном файле прототип шаблона и специализации, в другом заголовочном - реализация шаблона и специализации. Заголовочный с прототипами подключается к каждому файлу исходного кода (где есть вызоа шаблонной функции), а заголовочный с реализациями шаблона и специализации, только к первому файлу исходного кода.

Первый заголовочный "proba.h":
C++
1
2
3
4
5
6
7
8
#ifndef PROBA_H
#define PROBA_H
template <class T>
T max5(T m[], int x);
template <> const char* max5(const char* m[], int x);
void fun3();
void fun4();
#endif
Второй заголовочный "proba2.h":
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#ifndef PROBA_H_2
#define PROBA_H_2
template <class T>
T max5(T m[], int x)
{
    T p = m[0];
    for (int i = 1; i < x; i++)
    { 
        if (p < m[i]) p = m[i];
    }       
    return p;   
}
template <> const char* max5( const char* m[], int x)
{
    int n = 0;
    unsigned int p = strlen (m[0]);
    for (int i = 1; i < x; i++)
    if (p < strlen (m[i]))
    {
        p = strlen (m[i]);
        n = i;
    }
 
    return m[n];
}
#endif
Первый файл исходного кода:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
using namespace std;
#include "proba.h"
#include "proba2.h"
 
int main()
{
     int t[6] = {777, 34, 34, 66, 777, 1234};
         int l = max5(t, 6);
         cout << l << endl;
 
    double t2[4] = {7.98, 7.90, 3.676, 6.56};
    double l2 = max5(t2, 4);
        cout << l2 << endl;
        
        const char* uk[4] = {"sd","987654321", "qwertyu", "123456789"};
        const char* n = max5(uk, 4);
        cout  << n <<  endl;
        fun3();
        system ("pause");
    return 0;
}
Второй файл исходного кода:
C++
1
2
3
4
5
6
7
8
9
10
#include <iostream>
using namespace std;
#include "proba.h"
 
void fun3()
{
    int f2[3] = {12, -45, -2};
    int j1 = max5(f2, 3);
    cout << j1 << endl;
}
Третий файл исходного кода:
C++
1
2
3
4
5
6
7
8
9
10
#include <iostream>
using namespace std;
#include "proba.h"
 
void fun4()
{
    const char* uk[3] = {"sd","9876", "qwerty"};
    const char* n = max5(uk, 3);
    cout << n <<  endl;
}
При таком подходе всё нормально компонуется.
CAHTEXHUK
Заблокирован
22.09.2011, 09:14     Как правильно разделить файл в шаблонами? #5
в итоге ты вернулся к модели включения. Никакого смысла так разделять файлы нет, только ещё больше всё запутал
alsav22
5282 / 4801 / 442
Регистрация: 04.06.2011
Сообщений: 13,587
22.09.2011, 18:43  [ТС]     Как правильно разделить файл в шаблонами? #6
Согласен, что смысла нет. До настоящих смыслов я ещё не дошёл. Дошёл до главы "Раздельная компиляция". Там пишется:"...Таким образом, исходную программу можно разбить на три части:
- Заголовочный файл, содержащий объявления структур и прототипы функций, использующих эти структуры.
- Файл с исходным кодом, содержащий код функций, работающих со структурами.
- Файл с исходным кодом, содержащий код, осуществляющий вызовы этих функций...
Ещё:"...В заголовочных файлах обычно содержатся следующие элементы:
- Прототипы функций.
- Символьные константы, определённые с помощью директивы #define или const.
- Объявления структур.
- Объявления классов.
- Объявления шаблонов.
- Встроенные функции..."

Стал пробовать. С функциями, структурами всё получилось, а с шаблонами нет. Разделил прототип шаблона и определение его (как с функциями делал) не получается. Вот и возник вопрос. Написано, что в заголовочном следует разместить объявление шаблона. Под этим, скорее всего, подразумевается прототип шаблона вместе с определением.

Добавлено через 1 час 11 минут
Вроде бы разобрался. Получается так. Когда компилятор встречает в каком-либо файле исходного кода вызов шаблонной функции с некими аргументами, то он создаёт экземпляр этой функции под этот тип аргументов, а для этого, в этом файле должно присутствовать определение шаблона (получается, что у определения шаблона видимость в пределах файла). После этого, если компилятор встречает в другом файле исходного кода вызов шаблонной функции с таким же типом аргументов, то он использует ранее созданный экземпляр функции, и поэтому в этом файле ему уже не нужно определение шаблона (только прототип). Но что интересно, это не касается определения специализации шаблона. Она видна во всех файлах (внешнее связывание), и именно она давала ошибку двойного определения, если присутствовала в заголовочном файле и этот файл включался в несколько файлов исходного кода. Определение специализаци шаблона (так же как определения функций) нужно помещать в отдельный файл, а в заголовочном оставлять только прототипы и определения шаблонов. Тогда такой заголовочный файл можно включать в любое колличество файлов исходного кода.
Yandex
Объявления
22.09.2011, 18:43     Как правильно разделить файл в шаблонами?
Ответ Создать тему
Опции темы

Текущее время: 00:05. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru