0

我是 C++ 编程的新手,在执行单独编译时遇到了一个似乎无法解决的问题。我正在尝试专门化我的课程tokenize来为特定类型(istream)添加一个 dtor。我有以下内容:

#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 分离)一起工作。

4

1 回答 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() { /∗ ... ∗/ }

据我所知,一旦你明确专门化了一个类模板,你就创建了一个“普通类”。它显然不再是模板(您不能从专业化中创建类),而是<..>名称中带有 some 的类型。

在你的情况下,这只是意味着省略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

当 TU 需要专业化时,可能需要从类模板中生成它。这称为实例化。显式专门化的模板不会被隐式实例化(它已经专门化的)。也就是说,您的专业化tokenizer<ifstream>不必在任何 TU 中实例化。

由于这些原因,模板本身不适用于单独编译。但是,您可以使用显式实例化和显式特化来为模板化提供单独编译的好处。例如:

[头文件.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

[主.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 成语的东西:

[头文件.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}; }

[主.cpp]

int main()
{
    s obj;
    obj.f();
}
于 2013-10-26T20:53:25.370 回答