C ++ Class Template Специализация

0

Я новичок в программировании в C++, и я столкнулся с проблемой, которую я, похоже, не могу решить, применяя отдельную компиляцию. Я пытаюсь специализировать мой tokenize класса, чтобы добавить dtor для определенного типа (istream). У меня есть следующее:

#ifndef __TOKENIZER_H__
#define __TOKENIZER_H__

#include <fstream>
#include <string>

template <class T>
class base {
  // ... some methods/member variables.
};

template <class T>
class tokenizer : public base<T> {
  public:
    tokenizer(T &in);
};

template <>
class tokenizer<std::ifstream> : public base<std::ifstream> {
  public:
    tokenizer(std::ifstream &in);
    ~tokenizer();
};

#endif

... а также:

#include "tokenizer.h"

#include <fstream>
#include <iostream>
#include <locale>

using std::ifstream;
using std::istream;
using std::string;

// [BASE]

// ... code for those functions.

// [TOKENIZER]

// See header file.
template <class T>
tokenizer<T>::tokenizer(T &in) : base<T>(in) { }

// See header file.
template <>
tokenizer<ifstream>::tokenizer(ifstream &in) : base<ifstream>(in) { }

// See header file.
template <>
tokenizer<ifstream>::~tokenizer() {
  delete &(base<ifstream>::in);
}

// Intantiating template classes (separate compilation).
template class base<std::ifstream>;
template class base<std::istream>;
template class tokenizer<std::ifstream>;
template class tokenizer<std::istream>;

... однако я получаю следующую ошибку:

tokenizer.cc:62: error: template-id ‘tokenizer<> for ‘tokenizer<std::basic_ifstream<char, std::char_traits<char> > >::tokenizer(std::ifstream&) does not match any template declaration
tokenizer.cc:66: error: template-id ‘tokenizer<> for ‘tokenizer<std::basic_ifstream<char, std::char_traits<char> > >::~tokenizer() does not match any template declaration

Я компилирую с g++. Если кто-то может любезно отметить, что я пропал без вести и возможное объяснение, то это будет фантастично. Я смущен тем, как работают шаблоны с отдельной компиляцией (defns/decl отделено).

  • 0
    «Как шаблоны работают с отдельной компиляцией» - они почти не работают. "tokenizer.cc:62" - где строка 62?
  • 3
    IIRC в тот момент, когда вы объявляете явную специализацию tokenizer шаблона tokenizer , он становится как «нормальный класс». Оставьте template<> для определений ctor и dtor.
Показать ещё 7 комментариев
Теги:
templates

1 ответ

4
Лучший ответ

[temp.expl.spec]/5 указывает:

Члены явно специализированного шаблона класса определяются так же, как и члены обычных классов, и не используют синтаксис template<>. То же самое верно при определении члена явно специализированного класса-члена. Тем не менее, template<> используется для определения элемента явно специализированного шаблона класса членов, который специализируется как шаблон класса.

Он также предоставляет следующий пример (я приведу только некоторые выдержки):

template<class T> struct A {
    template<class U> struct C { };
};

template<> struct A<int> {
    void f(int);
};

// template<> not used for a member of an
// explicitly specialized class template
void A<int>::f(int) { /∗ ... ∗/ }

template<> template<class U> struct A<char>::C {
    void f();
};
// template<> is used when defining a member of an explicitly
// specialized member class template specialized as a class template
template<>
template<class U> void A<char>::C<U>::f() { /∗ ... ∗/ }

Насколько я знаю, после того, как вы создали специализированный шаблон класса, вы создали "обычный класс". Это явно не шаблон (вы не можете создавать классы из специализации), а тип с некоторым <..> в его имени.

В вашем случае это просто означает оставить template<> до

// See header file.
//template <>
tokenizer<ifstream>::tokenizer(ifstream &in) : base<ifstream>(in) { }

// See header file.
//template <>
tokenizer<ifstream>::~tokenizer() {
  delete &(base<ifstream>::in);
}

Что касается вашего запроса о разъяснении комбинации отдельной компиляции с шаблонами:

Когда вы используете шаблон класса для создания объекта (например, std::vector<int> v) или вызываете шаблон функции (например, std::sort(begin(v), end(v))), вы имеете дело с специализация шаблонов. std::vector<int> - это std::vector шаблона класса std::vector.

Если в TU требуется специализация, может потребоваться ее создание из шаблона класса. Это называется инстанцированием. Явный специализированный шаблон не будет инстанцироваться неявно (он уже специализирован). То есть, ваш tokenizer<ifstream> специализации tokenizer<ifstream> не должен создаваться в любом TU.

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

[Header.hpp]

template<class T> void foo(T);
extern template void foo<int>(int);

[Impl.cpp]

#include "header.hpp"

template<class T> void foo(T) { return T{} };

template void foo<int>(int); // force instantiation

[Main.cpp]

#include "header.hpp"

int main()
{
    foo<int>(42); // no instantiation will occur
}

В main.cpp мы не смогли создать экземпляр определения foo, поскольку определение недоступно. Мы могли бы создать декларацию. Также существует явное объявление о создании, которое предотвращает любые неявные экземпляры. В другом TU (impl.cpp) мы выполнили создание foo<int> через явное определение инстанцирования. Для этого требуется, чтобы определение f существовало и определяло определение. Остальное похоже на обычные функции: у нас есть два объявления и одно определение.

Аналогично, для шаблонов классов: если определение класса требуется в TU, нам нужно либо создать экземпляр шаблона, либо нам нужно иметь явную специализацию (явное определение инстанцирования здесь невозможно здесь AFAIK). Это пример OP.

Если определение класса не требуется, мы можем использовать что-то похожее на идиому PIMPL:

[Header.hpp]

template<class T>
class foobar;

struct s
{
    foobar<int>* p;
    void f();
}

[Impl.cpp]

#include "header.hpp"

template<class T> class foobar { int i; }

void s::f() { p = new foobar{42}; }

[Main.cpp]

int main()
{
    s obj;
    obj.f();
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню