将 C++ 模板函数定义存储在 .CPP 文件中
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/115703/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Storing C++ template function definitions in a .CPP file
提问by Rob
I have some template code that I would prefer to have stored in a CPP file instead of inline in the header. I know this can be done as long as you know which template types will be used. For example:
我有一些模板代码,我希望将它们存储在 CPP 文件中,而不是内联在标题中。我知道只要您知道将使用哪些模板类型,就可以做到这一点。例如:
.h file
.h 文件
class foo
{
public:
template <typename T>
void do(const T& t);
};
.cpp file
.cpp 文件
template <typename T>
void foo::do(const T& t)
{
// Do something with t
}
template void foo::do<int>(const int&);
template void foo::do<std::string>(const std::string&);
Note the last two lines - the foo::do template function is only used with ints and std::strings, so those definitions mean the app will link.
请注意最后两行 - foo::do 模板函数仅用于 ints 和 std::strings,因此这些定义意味着应用程序将链接。
My question is - is this a nasty hack or will this work with other compilers/linkers? I am only using this code with VS2008 at the moment but will be wanting to port to other environments.
我的问题是 - 这是一个讨厌的黑客还是可以与其他编译器/链接器一起使用?我目前仅在 VS2008 中使用此代码,但希望移植到其他环境。
采纳答案by Aaron N. Tubbs
The problem you describe can be solved by defining the template in the header, or via the approach you describe above.
您描述的问题可以通过在标题中定义模板来解决,或者通过您上面描述的方法来解决。
I recommend reading the following points from the C++ FAQ Lite:
我建议阅读C++ FAQ Lite 中的以下几点:
- Why can't I separate the definition of my templates class from its declaration and put it inside a .cpp file?
- How can I avoid linker errors with my template functions?
- How does the C++ keyword export help with template linker errors?
They go into a lot of detail about these (and other) template issues.
他们详细介绍了这些(和其他)模板问题。
回答by namespace sid
For others on this page wondering what the correct syntax is (as did I) for explicit template specialisation (or at least in VS2008), its the following...
对于此页面上的其他人想知道显式模板专业化(或至少在 VS2008 中)的正确语法是什么(就像我一样),其如下...
In your .h file...
在您的 .h 文件中...
template<typename T>
class foo
{
public:
void bar(const T &t);
};
And in your .cpp file
在你的 .cpp 文件中
template <class T>
void foo<T>::bar(const T &t)
{ }
// Explicit template instantiation
template class foo<int>;
回答by Konrad Rudolph
This code is well-formed. You only have to pay attention that the definition of the template is visible at the point of instantiation. To quote the standard, § 14.7.2.4:
此代码格式良好。您只需要注意模板的定义在实例化点是可见的。引用标准,第 14.7.2.4 节:
The definition of a non-exported function template, a non-exported member function template, or a non-exported member function or static data member of a class template shall be present in every translation unit in which it is explicitly instantiated.
非导出函数模板、非导出成员函数模板或类模板的非导出成员函数或静态数据成员的定义应存在于其显式实例化的每个翻译单元中。
回答by moonshadow
This should work fine everywhere templates are supported. Explicit template instantiation is part of the C++ standard.
这应该可以在任何支持模板的地方正常工作。显式模板实例化是 C++ 标准的一部分。
回答by Cameron Tacklind
Your example is correct but not very portable. There is also a slightly cleaner syntax that can be used (as pointed out by @namespace-sid).
你的例子是正确的,但不是很便携。还可以使用更简洁的语法(如@namespace-sid 所指出的)。
Suppose the templated class is part of some library that is to be shared. Should other versions of the templated class be compiled? Is the library maintainer supposed to anticipate all possible templated uses of the class?
假设模板化类是某个要共享的库的一部分。是否应该编译其他版本的模板化类?库维护者是否应该预测该类的所有可能的模板化用途?
An alternate approach is a slight variation on what you have: add a third file that is the template implementation/instantiation file.
另一种方法是对您拥有的内容略有不同:添加第三个文件,即模板实现/实例化文件。
foo.h file
foo.h 文件
// Standard header file guards omitted
template <typename T>
class foo
{
public:
void bar(const T& t);
};
foo.cpp file
foo.cpp 文件
// Always include your headers
#include "foo.h"
template <typename T>
void foo::bar(const T& t)
{
// Do something with t
}
foo-impl.cpp file
foo-impl.cpp 文件
// Yes, we include the .cpp file
#include "foo.cpp"
template class foo<int>;
The one caveat is that you need to tell the compiler to compile foo-impl.cpp
instead of foo.cpp
as compiling the latter does nothing.
一个警告是您需要告诉编译器进行编译,foo-impl.cpp
而不是foo.cpp
因为编译后者什么都不做。
Of course, you can have multiple implementations in the third file or have multiple implementation files for each type you'd like to use.
当然,您可以在第三个文件中有多个实现,或者为您想要使用的每种类型有多个实现文件。
This enables much more flexibility when sharing the templated class for other uses.
这在共享模板类用于其他用途时提供了更大的灵活性。
This setup also reduces compile times for reused classes because you're not recompiling the same header file in each translation unit.
此设置还减少了重用类的编译时间,因为您无需在每个翻译单元中重新编译相同的头文件。
回答by Red XIII
This is definitely not a nasty hack, but be aware of the fact that you will have to do it (the explicit template specialization) for every class/type you want to use with the given template. In case of MANY types requesting template instantiation there can be A LOT of lines in your .cpp file. To remedy this problem you can have a TemplateClassInst.cpp in every project you use so that you have greater control what types will be instantiated. Obviously this solution will not be perfect (aka silver bullet) as you might end up breaking the ODR :).
这绝对不是一个讨厌的黑客,但请注意,您必须为要与给定模板一起使用的每个类/类型执行此操作(显式模板特化)。在许多类型请求模板实例化的情况下,您的 .cpp 文件中可能会有很多行。为了解决这个问题,你可以在你使用的每个项目中都有一个 TemplateClassInst.cpp,这样你就可以更好地控制将要实例化的类型。显然,这个解决方案并不完美(又名银弹),因为您最终可能会破坏 ODR :)。
回答by Red XIII
That is a standard way to define template functions. I think there are three methods I read for defining templates. Or probably 4. Each with pros and cons.
这是定义模板函数的标准方法。我认为我阅读了三种定义模板的方法。或者可能 4. 各有利弊。
Define in class definition. I don't like this at all because I think class definitions are strictly for reference and should be easy to read. However it is much less tricky to define templates in class than outside. And not all template declarations are on the same level of complexity. This method also makes the template a true template.
Define the template in the same header, but outside of the class. This is my preferred way most of the times. It keeps your class definition tidy, the template remains a true template. It however requires full template naming which can be tricky. Also, your code is available to all. But if you need your code to be inline this is the only way. You can also accomplish this by creating a .INL file at the end of your class definitions.
Include the header.h and implementation.CPP into your main.CPP. I think that's how its done. You won't have to prepare any pre instantiations, it will behave like a true template. The problem I have with it is that it is not natural. We don't normally include and expect to include source files. I guess since you included the source file, the template functions can be inlined.
This last method, which was the posted way, is defining the templates in a source file, just like number 3; but instead of including the source file, we pre instantiate the templates to ones we will need. I have no problem with this method and it comes in handy sometimes. We have one big code, it cannot benefit from being inlined so just put it in a CPP file. And if we know common instantiations and we can predefine them. This saves us from writing basically the same thing 5, 10 times. This method has the benefit of keeping our code proprietary. But I don't recommend putting tiny, regularly used functions in CPP files. As this will reduce the performance of your library.
在类定义中定义。我根本不喜欢这样,因为我认为类定义仅供参考,并且应该易于阅读。然而,在类中定义模板比在外部定义要简单得多。并不是所有的模板声明都处于同一级别的复杂性。此方法还使模板成为真正的模板。
在同一个标题中定义模板,但在类之外。大多数时候这是我的首选方式。它使您的类定义保持整洁,模板仍然是真正的模板。然而,它需要完整的模板命名,这可能很棘手。此外,您的代码可供所有人使用。但是,如果您需要内联代码,这是唯一的方法。您还可以通过在类定义的末尾创建一个 .INL 文件来完成此操作。
将 header.h 和 implementation.CPP 包含到 main.CPP 中。我认为它就是这样做的。您不必准备任何预实例化,它的行为就像一个真正的模板。我的问题是它不自然。我们通常不包含也不期望包含源文件。我猜因为您包含了源文件,所以可以内联模板函数。
最后一种方法是发布的方式,它是在源文件中定义模板,就像数字 3 一样;但是我们不包含源文件,而是将模板预先实例化为我们需要的模板。我对这种方法没有问题,有时它会派上用场。我们有一个大代码,它不能从内联中受益,所以只需将它放在一个 CPP 文件中。如果我们知道常见的实例并且我们可以预定义它们。这使我们免于写基本相同的东西 5、10 次。这种方法的好处是保持我们的代码专有。但我不建议在 CPP 文件中放置微小的、经常使用的函数。因为这会降低库的性能。
Note, I am not aware of the consequences of a bloated obj file.
请注意,我不知道 obj 文件膨胀的后果。
回答by Ben Collins
There is, in the latest standard, a keyword (export
) that would help alleviate this issue, but it isn't implemented in any compiler that I'm aware of, other than Comeau.
在最新的标准中,有一个关键字 ( export
) 可以帮助缓解这个问题,但它没有在我知道的任何编译器中实现,除了 Comeau。
See the FAQ-liteabout this.
请参阅有关此问题的 FAQ-lite。
回答by Lou Franco
Yes, that's the standard way to do specializiationexplicit instantiation. As you stated, you cannot instantiate this template with other types.
是的,这是进行专业化显式实例化的标准方法。正如您所说,您不能使用其他类型实例化此模板。
Edit: corrected based on comment.
编辑:根据评论更正。
回答by TarmoPikaro
Let's take one example, let's say for some reason you want to have a template class:
让我们举一个例子,假设出于某种原因你想要一个模板类:
//test_template.h:
#pragma once
#include <cstdio>
template <class T>
class DemoT
{
public:
void test()
{
printf("ok\n");
}
};
template <>
void DemoT<int>::test()
{
printf("int test (int)\n");
}
template <>
void DemoT<bool>::test()
{
printf("int test (bool)\n");
}
If you compile this code with Visual Studio - it works out of box. gcc will produce linker error (if same header file is used from multiple .cpp files):
如果您使用 Visual Studio 编译此代码 - 它开箱即用。gcc 将产生链接器错误(如果从多个 .cpp 文件使用相同的头文件):
error : multiple definition of `DemoT<int>::test()'; your.o: .../test_template.h:16: first defined here
It's possible to move implementation to .cpp file, but then you need to declare class like this -
可以将实现移动到 .cpp 文件,但是您需要像这样声明类 -
//test_template.h:
#pragma once
#include <cstdio>
template <class T>
class DemoT
{
public:
void test()
{
printf("ok\n");
}
};
template <>
void DemoT<int>::test();
template <>
void DemoT<bool>::test();
// Instantiate parametrized template classes, implementation resides on .cpp side.
template class DemoT<bool>;
template class DemoT<int>;
And then .cpp will look like this:
然后 .cpp 将如下所示:
//test_template.cpp:
#include "test_template.h"
template <>
void DemoT<int>::test()
{
printf("int test (int)\n");
}
template <>
void DemoT<bool>::test()
{
printf("int test (bool)\n");
}
Without two last lines in header file - gcc will work fine, but Visual studio will produce an error:
如果头文件中没有最后两行 - gcc 可以正常工作,但 Visual Studio 会产生错误:
error LNK2019: unresolved external symbol "public: void __cdecl DemoT<int>::test(void)" (?test@?$DemoT@H@@QEAAXXZ) referenced in function
template class syntax is optional in case if you want to expose function via .dll export, but this is applicable only for windows platform - so test_template.h could look like this:
如果您想通过 .dll 导出公开函数,模板类语法是可选的,但这仅适用于 Windows 平台 - 因此 test_template.h 可能如下所示:
//test_template.h:
#pragma once
#include <cstdio>
template <class T>
class DemoT
{
public:
void test()
{
printf("ok\n");
}
};
#ifdef _WIN32
#define DLL_EXPORT __declspec(dllexport)
#else
#define DLL_EXPORT
#endif
template <>
void DLL_EXPORT DemoT<int>::test();
template <>
void DLL_EXPORT DemoT<bool>::test();
with .cpp file from previous example.
使用上一个示例中的 .cpp 文件。
This however gives more headache to linker, so it's recommended to use previous example if you don't export .dll function.
然而,这会给链接器带来更多麻烦,因此如果您不导出 .dll 函数,建议使用前面的示例。