模板不必在标题中!如果您只有几个实例化,您可以在合适的翻译单元中显式实例化类和函数模板。也就是说,模板将分为三个部分:
- 声明模板的标头。
- 包含第一个和实现模板的标题,但仅包含在第三组文件中。
- 源文件,包括 2. 中的标头,并显式实例化具有相应类型的模板。
这些模板的用户只会包含标头,而不会包含实现标头。可以做到这一点的一个例子是 IOStreams:基本上只有两个实例化:一个 forchar
和一个 for wchar_t
。是的,您可以实例化其他类型的流,但我怀疑有人会这样做(我有时会质疑是否有人使用具有不同字符类型的流,char
但可能是人们)。
也就是说,模板使用的概念确实没有在源代码中明确表示,C++11 也没有添加任何工具来这样做。有关于向 C++ 添加概念的讨论,但到目前为止它们还不是任何标准的一部分。我认为有一个概念轻量级提案将包含在 C++14 中。
但是,在实践中,我并没有发现太大的问题:很有可能记录概念并使用诸如static_assert()
潜在地产生更好的错误消息之类的东西。问题在于许多概念实际上比底层算法更具限制性,而且额外的松弛有时非常有用。
这是一个关于如何实现和实例化模板的简短且有些虚构的示例。这个想法是实现类似std::basic_ostream
但仅提供字符串输出运算符的缩小版本:
// simple-ostream.hpp
#include "simple-streambuf.hpp"
template <typename CT>
class simple_ostream {
simple_streambuf<CT>* d_sbuf;
public:
simple_ostream(simple_streambuf<CT>* sbuf);
simple_streambuf<CT>* rdbuf() { return this->d_sbuf; } // should be inline
};
template <typename CT>
simple_ostream<CT>& operator<< (simple_ostream<CT>&, CT const*);
除了rdbuf()
成员之外,上面只是一个带有几个成员声明和一个函数声明的类定义。该rdbuf()
功能是直接实现的,以表明您可以将需要性能的可见实现与解耦更重要的外部实现混合和匹配。使用的类模板simple_streambuf
被认为类似于std::basic_streambuf
并且至少在 header 中声明"simple-streambuf.hpp"
。
// simple-ostream.tpp
// the implementation, only included to create explicit instantiations
#include "simple-ostream.hpp"
template <typename CT>
simple_ostream<CT>::simple_ostream(simple_streambuf<CT>* sbuf): d_sbuf(sbuf) {}
template <typename CT>
simple_ostream<CT>& operator<< (simple_ostream<CT>& out, CT const* str) {
for (; *str; ++str) {
out.rdbuf()->sputc(*str);
}
return out;
}
仅在显式实例化类和函数模板时才包含此实现标头。例如,for 的实例化char
如下所示:
// simple-ostream-char.cpp
#include "simple-ostream.tpp"
// instantiate all class members for simple_ostream<char>:
template class simple_ostream<char>;
// instantiate the free-standing operator
template simple_ostream<char>& operator<< <char>(simple_ostream<char>&, char const*);
的任何使用simple_ostream<CT>
都只会包括simple-ostream.hpp
. 例如:
// use-simple-ostream.cpp
#include "simple-ostream.hpp"
int main()
{
simple_streambuf<char> sbuf;
simple_ostream<char> out(&sbuf);
out << "hello, world\n";
}
当然,要构建可执行文件,您将需要两者use-simple-ostream.o
,simple-ostream-char.o
但假设模板实例化是库的一部分,这并没有真正增加任何复杂性。唯一真正令人头疼的是,当用户想要使用具有意外实例化的类模板时,例如char16_t
,但只提供了char
和wchar_t
:在这种情况下,用户需要显式创建实例化,或者,如果需要,包括实现头文件。
如果你想尝试这个例子,下面是一个有点简单和草率的(因为只是标题)的实现simple-streambuf<CT>
:
#ifndef INCLUDED_SIMPLE_STREAMBUF
#define INCLUDED_SIMPLE_STREAMBUF
#include <iostream>
template <typename CT> struct stream;
template <>
struct stream<char> {
static std::ostream& get() { return std::cout; }
};
template <>
struct stream<wchar_t> {
static std::wostream& get() { return std::wcout; }
};
template <typename CT>
struct simple_streambuf
{
void sputc(CT c) {
stream<CT>::get().rdbuf()->sputc(c);
}
};
#endif