使用 header 和源文件模板化 类
Templated Classes using header and source file
经过大量研究,我明白了为什么模板 类 不能传统上分为 header 和源文件。
但是,这个 (Why can templates only be implemented in the header file?) 似乎暗示您仍然可以通过在 header 文件末尾包含实现文件来进行某种 pseudo-separate-compilation 过程,如图所示以下:
// Foo.h
template <typename T>
struct Foo
{
void doSomething(T param);
};
#include "Foo.tpp"
// Foo.tpp
template <typename T>
void Foo<T>::doSomething(T param)
{
//implementation
}
有说明"A common solution to this is to write the template declaration in a header file, then implement the class in an implementation file (for example .tpp), and include this implementation file at the end of the header."
然而,当我这样做时,我收到多个错误,指出它是 constructor/method/function 的重新定义。我已经能够通过在 .cpp 文件上放置 include guards 来防止这种情况,这似乎是一种不好的做法。
我的主要问题是:
使用 header 文件底部的实现包含的正确方法是什么?
我的 .cpp 文件中的包含保护是否会阻止编译器为其他类型创建模板class/function,因为它现在只能包含一次?
首先使用 header 文件的部分原因不是为了防止每次包含代码时都重新复制代码,以保持较短的编译时间吗?那么模板函数(因为它们必须在 header 中定义)与简单地重载 function/class 的性能影响是什么?应该在什么时候使用?
下面是我自己的简单 Node 结构的代码缩减版:
// Node.hpp
#ifndef NODES_H
#define NODES_H
#include <functional>
namespace nodes
{
template<class Object>
struct Node
{
public:
Node(Object value, Node* link = nullptr);
void append(Node tail);
Object data;
Node* next;
};
template<class Object> void prepend(Node<Object>*& headptr, Node<Object> newHead);
}
// Forward 'declaration' for hash specialization
namespace std
{
template <typename Object>
struct hash<nodes::Node<Object>>;
}
#include "Node.cpp"
#endif
// Node.cpp
// #ifndef NODE_CPP
// #define NODE_CPP
#include "Node.hpp"
template<class Object>
nodes::Node<Object>::Node(Object value, Node* link): data(value), next(link) {}
template<class Object>
void nodes::Node<Object>::append(Node tail) {
Node* current = this;
while (current->next != nullptr) {
current = current->next;
}
current->next = &tail;
}
template<class Object>
void nodes::prepend(Node<Object>*& headptr, Node<Object> newHead) {
Node<Object>* newHeadPtr = &newHead;
Node<Object>* temporary = newHeadPtr;
while (temporary->next != nullptr) {
temporary = temporary->next;
}
temporary->next = headptr;
headptr = newHeadPtr;
}
namespace std
{
template <typename Object>
struct hash<nodes::Node<Object>>
{
size_t operator()(nodes::Node<Object>& node) const
{
return hash<Object>()(node.data);
}
};
}
// #endif
What is the proper way to do this with the implementation include at the bottom of the header file?
将 include guards 放入您的 header 文件中,包括实施 #include
指令:
#ifndef __FOO_H
#define __FOO_H
// Foo.h
template <typename T>
struct Foo
{
void doSomething(T param);
};
#include "Foo.tpp"
#endif
您也可以将守卫添加到 Foo.tpp
,但在您发布的情况下,它没有多大意义。
Would the include guards on my .cpp file prevent the compiler from creating the templates class/function for other types since it can now only be included once?
通常你根本不需要在 *.cpp
文件中包含守卫,因为你不会在任何地方包含它们。只有那些包含在多个翻译单元中的文件才需要包含守卫。当然,这些守卫不会阻止实例化其他类型的模板,因为这是设计模板的目的。
Isn't part of the reason for using header files in the first place is to prevent code from being recopied every time it is included, to keep a short compilation time? So what is the performance effect of templated functions (since they have to be defined in header) versus simply overloading a function/class? When should each be used?
这里你提出了一个大的,platform-dependent 和一点 opinion-based 的话题。正如您所说,从历史上看,包含文件用于防止代码复制。将函数声明(headers,而不是定义)包含到多个翻译单元中就足够了,然后 link 它们与包含函数的编译代码的单个副本。
模板编译比 non-template 函数慢得多,因此实现模板导出(单独 header/implementation 模板编译)不值得节省编译时间。
有关模板性能的一些讨论和好的答案,请查看这些问题:
- Is Template Metaprogramming faster than the equivalent C code?
- C++ templates for performance?
- Do c++ templates make programs slow?
简而言之,有时模板允许您在编译时而不是 运行 时做出一些决定,从而使代码更快。无论如何,确定代码是否变得更快的唯一正确方法是在 real-world 环境中进行 运行 性能测试。
最后,模板更多的是关于设计,而不是性能。它们允许您显着减少代码重复并符合 DRY 原则。一个平庸的例子是像 std::max. A more interesting example is Boost.Spirit 这样的函数,它使用模板完全在编译时构建解析器。
经过大量研究,我明白了为什么模板 类 不能传统上分为 header 和源文件。
但是,这个 (Why can templates only be implemented in the header file?) 似乎暗示您仍然可以通过在 header 文件末尾包含实现文件来进行某种 pseudo-separate-compilation 过程,如图所示以下:
// Foo.h
template <typename T>
struct Foo
{
void doSomething(T param);
};
#include "Foo.tpp"
// Foo.tpp
template <typename T>
void Foo<T>::doSomething(T param)
{
//implementation
}
有说明"A common solution to this is to write the template declaration in a header file, then implement the class in an implementation file (for example .tpp), and include this implementation file at the end of the header."
然而,当我这样做时,我收到多个错误,指出它是 constructor/method/function 的重新定义。我已经能够通过在 .cpp 文件上放置 include guards 来防止这种情况,这似乎是一种不好的做法。
我的主要问题是:
使用 header 文件底部的实现包含的正确方法是什么?
我的 .cpp 文件中的包含保护是否会阻止编译器为其他类型创建模板class/function,因为它现在只能包含一次?
首先使用 header 文件的部分原因不是为了防止每次包含代码时都重新复制代码,以保持较短的编译时间吗?那么模板函数(因为它们必须在 header 中定义)与简单地重载 function/class 的性能影响是什么?应该在什么时候使用?
下面是我自己的简单 Node 结构的代码缩减版:
// Node.hpp
#ifndef NODES_H
#define NODES_H
#include <functional>
namespace nodes
{
template<class Object>
struct Node
{
public:
Node(Object value, Node* link = nullptr);
void append(Node tail);
Object data;
Node* next;
};
template<class Object> void prepend(Node<Object>*& headptr, Node<Object> newHead);
}
// Forward 'declaration' for hash specialization
namespace std
{
template <typename Object>
struct hash<nodes::Node<Object>>;
}
#include "Node.cpp"
#endif
// Node.cpp
// #ifndef NODE_CPP
// #define NODE_CPP
#include "Node.hpp"
template<class Object>
nodes::Node<Object>::Node(Object value, Node* link): data(value), next(link) {}
template<class Object>
void nodes::Node<Object>::append(Node tail) {
Node* current = this;
while (current->next != nullptr) {
current = current->next;
}
current->next = &tail;
}
template<class Object>
void nodes::prepend(Node<Object>*& headptr, Node<Object> newHead) {
Node<Object>* newHeadPtr = &newHead;
Node<Object>* temporary = newHeadPtr;
while (temporary->next != nullptr) {
temporary = temporary->next;
}
temporary->next = headptr;
headptr = newHeadPtr;
}
namespace std
{
template <typename Object>
struct hash<nodes::Node<Object>>
{
size_t operator()(nodes::Node<Object>& node) const
{
return hash<Object>()(node.data);
}
};
}
// #endif
What is the proper way to do this with the implementation include at the bottom of the header file?
将 include guards 放入您的 header 文件中,包括实施 #include
指令:
#ifndef __FOO_H
#define __FOO_H
// Foo.h
template <typename T>
struct Foo
{
void doSomething(T param);
};
#include "Foo.tpp"
#endif
您也可以将守卫添加到 Foo.tpp
,但在您发布的情况下,它没有多大意义。
Would the include guards on my .cpp file prevent the compiler from creating the templates class/function for other types since it can now only be included once?
通常你根本不需要在 *.cpp
文件中包含守卫,因为你不会在任何地方包含它们。只有那些包含在多个翻译单元中的文件才需要包含守卫。当然,这些守卫不会阻止实例化其他类型的模板,因为这是设计模板的目的。
Isn't part of the reason for using header files in the first place is to prevent code from being recopied every time it is included, to keep a short compilation time? So what is the performance effect of templated functions (since they have to be defined in header) versus simply overloading a function/class? When should each be used?
这里你提出了一个大的,platform-dependent 和一点 opinion-based 的话题。正如您所说,从历史上看,包含文件用于防止代码复制。将函数声明(headers,而不是定义)包含到多个翻译单元中就足够了,然后 link 它们与包含函数的编译代码的单个副本。
模板编译比 non-template 函数慢得多,因此实现模板导出(单独 header/implementation 模板编译)不值得节省编译时间。
有关模板性能的一些讨论和好的答案,请查看这些问题:
- Is Template Metaprogramming faster than the equivalent C code?
- C++ templates for performance?
- Do c++ templates make programs slow?
简而言之,有时模板允许您在编译时而不是 运行 时做出一些决定,从而使代码更快。无论如何,确定代码是否变得更快的唯一正确方法是在 real-world 环境中进行 运行 性能测试。
最后,模板更多的是关于设计,而不是性能。它们允许您显着减少代码重复并符合 DRY 原则。一个平庸的例子是像 std::max. A more interesting example is Boost.Spirit 这样的函数,它使用模板完全在编译时构建解析器。