zhuravlevkk
@zhuravlevkk
Инженер-программист

Можно ли вынести реализацию шаблонного класса в отдельный cpp файл?

Господа, при компиляции вылетает ошибка C2446. Что я делаю не так?

myStack.h
#pragma once
// Стек на динамическом массиве
//////////////////////////////////////////////////
template <typename TElement>
class stackM
{
	size_t count;
	TElement *m;

public:
	stackM(size_t maxCount = 1024);
	~stackM();
	void push(const TElement &x);
	void pop();
	TElement top();
	size_t size();
};

//////////////////////////////////////////////////
// Стек на односвязном списке
//////////////////////////////////////////////////
template <typename TElement>
struct Node
{
	TElement data;
	Node<TElement> next;
	Node(const TElement &d, Node <TElement> *n)
	{
		data = d;
		next = n;
	}
};

template <typename TElement>
class stackL
{
	size_t count;
	Node<TElement> *Top;

public:
	stackL();
	void push(const TElement &x);
	void pop();
	TElement top();
	size_t size();
};
//////////////////////////////////////////////////

myStack.cpp
#include "myStack.h"
// Стек на динамическом массиве
//////////////////////////////////////////////////
template <typename TElement>
stackM<TElement>::stackM(size_t maxCount = 1024)
{
	count = 0;
	m = new TElement[maxCount];
}

template <typename TElement>
stackM<TElement>::~stackM()
{
	delete[]m;
}

template <typename TElement>
void stackM<TElement>::push(const TElement &x)
{
	m[count++] = x;
}

template <typename TElement>
void stackM<TElement>::pop()
{
	--count;
}

template <typename TElement>
TElement stackM<TElement>::top()
{
	return m[count-1];
}

template <typename TElement>
size_t stackM<TElement>::size()
{
	return count;
}
//////////////////////////////////////////////////

//////////////////////////////////////////////////
// Стек на односвязном списке
//////////////////////////////////////////////////

template <typename TElement>
stackL<TElement>::stackL()
{
	count = 0;
	top = nullptr;
}

template <typename TElement>
void stackL<TElement>::push(const TElement &x)
{
	auto newT = new Node(x, top);
}

template <typename TElement>
void stackL<TElement>::pop()
{
	auto tmp = Top->next;
	delete Top;
	Top = tmp;
	--count;
}

template <typename TElement>
TElement stackL<TElement>::top()
{
	return data;
}

template <typename TElement>
size_t stackL<TElement>::size()
{
	return count;
}
//////////////////////////////////////////////////

main.cpp
#include <iostream>
#include "myStack.h"

using namespace std;

int main(void)
{
	stackM <int> massiv(200);
	for (int i = 0; i < 5; i++)	massiv.push(i);
	for (size_t i = 0; i < massiv.size; i++) cout << massiv.top() << endl;
	return 0;
}
  • Вопрос задан
  • 2091 просмотр
Решения вопроса 3
maaGames
@maaGames
Погроммирую программы
Можно. Но при условии, что вручную будут инстанцированы шаблоны для каждого из используемых типов.
Ответ написан
Комментировать
tsarevfs
@tsarevfs Куратор тега C++
C++ developer
Если кратко, то нельзя.

Шаблонны в С++ генерируют свою версию кода для каждого варианта параметров, с которыми их используют. Если у вас есть класс A, и вы используете A и A, то будет создано 2 набора методов этого класса, для каждого типа.
Вспоминаем, что cpp файлы компилируются отдельно.
Мы можем собирать a.cpp -> a.obj, потом main.cpp -> main.obj, а потом слинковать их (a.obj, main.obj) -> main.exe.
include же просто копипастит текст файла вместо строчки #include "..."
Теперь представим, что мы определили класс A в a.cpp а используем main.cpp. Но в этом случае мы просто не узнаем о том что класс используют в main.cpp при компиляции a.cpp, и не сгенерируем нужные версии методов.
Ответ написан
Комментировать
@Fil
В целом - нельзя. Когда компилируется myStack.cpp, компилятор не знает, что ему подставить вместо TElement, поэтому имплементация методов не создается.
Когда компилируется main.cpp, то компилятор знает, что тип int, и подставляет вместо massiv.push(i) что-то вроде stackM__int__push(massiv, i), предполагая, что stackM__int__push уже создан в каком-то другом скомпилированном obj-файле.
Ошибок компиляции тут нет (не знаю почему у вас C2446). Но затем компоновщик нигде не может найти stackM__int__push, чтобы подставить вместо нее конкретный адрес и ругается.
Здесь есть подробное объяснение, и предлагаются различные костыли для решения, но лучше просто определять в h-файле.
Еще у вас ошибка в main.cpp: "massiv.size". size - это функция: "massiv.size()"
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
@Mercury13
Программист на «си с крестами» и не только
Можно в таких случаях.
1. Если у шаблона ровно N специализаций и (N+1)-й быть не может.
2. Если он private и его синтаксически невозможно вызвать откуда-то ещё, кроме CPP.
3. template<> (полностью специализированная функция без шаблонных параметров). Ей место именно в CPP, если она общедоступная и не inline.

Стек, очевидно, ни к одному из этих случаев не относится.

Код создаёт не сам шаблон, а его расшаблонивание!
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы