C++ 如何创建对模板类执行操作的静态模板成员函数?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/488959/
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
How do you create a static template member function that performs actions on a template class?
提问by bsruth
I'm trying to create a generic function that removes duplicates from an std::vector. Since I don't want to create a function for each vector type, I want to make this a template function that can accept vectors of any type. Here is what I have:
我正在尝试创建一个从 std::vector 中删除重复项的通用函数。由于我不想为每种向量类型创建一个函数,我想让它成为一个可以接受任何类型向量的模板函数。这是我所拥有的:
//foo.h
Class Foo {
template<typename T>
static void RemoveVectorDuplicates(std::vector<T>& vectorToUpdate);
};
//foo.cpp
template<typename T>
void Foo::RemoveVectorDuplicates(std::vector<T>& vectorToUpdate) {
for(typename T::iterator sourceIter = vectorToUpdate.begin(); (sourceIter != vectorToUpdate.end() - 1); sourceIter++) {
for(typename T::iterator compareIter = (vectorToUpdate.begin() + 1); compareIter != vectorToUpdate.end(); compareIter++) {
if(sourceIter == compareIter) {
vectorToUpdate.erase(compareIter);
}
}
}
}
//SomeOtherClass.cpp
#include "foo.h"
...
void SomeOtherClass::SomeFunction(void) {
std::vector<int> myVector;
//fill vector with values
Foo::RemoveVectorDuplicates(myVector);
}
I keep getting a linker error, but it compiles fine. Any ideas as to what I'm doing wrong?
我不断收到链接器错误,但它编译得很好。关于我做错了什么的任何想法?
UPDATE: Based on the answer given by Iraimbilanja, I went and rewrote the code. However, just in case someone wanted working code to do the RemoveDuplicates function, here it is:
更新:根据 Iraimbilanja 给出的答案,我去重写了代码。但是,以防万一有人想要工作代码来执行 RemoveDuplicates 函数,这里是:
//foo.h
Class Foo {
template<typename T>
static void RemoveVectorDuplicates(T& vectorToUpdate){
for(typename T::iterator sourceIter = vectorToUpdate.begin(); sourceIter != vectorToUpdate.end(); sourceIter++) {
for(typename T::iterator compareIter = (sourceIter + 1); compareIter != vectorToUpdate.end(); compareIter++) {
if(*sourceIter == *compareIter) {
compareIter = vectorToUpdate.erase(compareIter);
}
}
}
};
Turns out that if I specify std::vector in the signature, the iterators don't work correctly. So I had to go with a more generic approach. Also, when erasing compareIter, the next iteration of the loop produces a pointer exception. The post decrement of compareIter on an erase takes care of that problem. I also fixed the bugs in the iterator compare and in the initialization of compareIter in the 2nd loop.
事实证明,如果我在签名中指定 std::vector,迭代器将无法正常工作。所以我不得不采用更通用的方法。此外,在擦除 compareIter 时,循环的下一次迭代会产生指针异常。擦除后 compareIter 的递减处理解决了这个问题。我还修复了迭代器比较和第二个循环中 compareIter 初始化中的错误。
UPDATE 2:
更新 2:
I saw that this question got another up vote, so figured I'd update it with a better algorithm that uses some C++14 goodness. My previous one only worked if the type stored in the vector implemented operator== and it required a bunch of copies and unnecessary comparisons. And, in hindsight, there is no need to make it a member of a class. This new algorithm allows for a custom compare predicate, shrinks the compare space as duplicates are found and makes a significantly smaller number of copies. The name has been changed to erase_duplicates
to better conform to STL algorithm naming conventions.
我看到这个问题得到了另一个投票,所以我想我会用一个更好的算法更新它,它使用一些 C++14 的优点。我之前的一个只在存储在向量中的类型实现了 operator== 并且需要一堆副本和不必要的比较时才有效。而且,事后看来,没有必要让它成为类的成员。这种新算法允许自定义比较谓词,在发现重复项时缩小比较空间,并显着减少副本数量。该名称已更改为erase_duplicates
更好地符合 STL 算法命名约定。
template<typename T>
static void erase_duplicates(T& containerToUpdate)
{
erase_duplicates(containerToUpdate, nullptr);
}
template<typename T>
static void erase_duplicates(T& containerToUpdate,
std::function<bool (typename T::value_type const&, typename T::value_type const&)> pred)
{
auto lastNonDuplicateIter = begin(containerToUpdate);
auto firstDuplicateIter = end(containerToUpdate);
while (lastNonDuplicateIter != firstDuplicateIter) {
firstDuplicateIter = std::remove_if(lastNonDuplicateIter + 1, firstDuplicateIter,
[&lastNonDuplicateIter, &pred](auto const& compareItem){
if (pred != nullptr) {
return pred(*lastNonDuplicateIter, compareItem);
}
else {
return *lastNonDuplicateIter == compareItem;
}
});
++lastNonDuplicateIter;
}
containerToUpdate.erase(firstDuplicateIter, end(containerToUpdate));
}
回答by
Short Answer
简答
Define the function in the header, preferably inside the class definition.
在头文件中定义函数,最好在类定义中。
Long answer
长答案
Defining the template function inside the .cpp means it won't get #include
d into any translation units: it will only be available to the translation unit it's defined in.
在 .cpp 中定义模板函数意味着它不会#include
进入任何翻译单元:它只对定义它的翻译单元可用。
Hence RemoveVectorDuplicates
must be defined in the header, as this is the only way the compiler can text-substitute the template arguments, hence instantiatingthe template, producing an usable class.
因此RemoveVectorDuplicates
必须在头文件中定义,因为这是编译器可以文本替换模板参数的唯一方法,从而实例化模板,生成可用的类。
There are two workarounds for this inconvenience
对于这种不便,有两种解决方法
First, you can remove the #include "foo.h"
from the .cpp and add another one, in the endof the header:
首先,您可以#include "foo.h"
从 .cpp 中删除并在标题末尾添加另一个:
#include "foo.cpp"
This lets you organize your files consistently, but doesn't provide the usual advantages of separate compilation (smaller dependencies, faster and rarer compiles).
这使您可以一致地组织文件,但不会提供单独编译的通常优势(更小的依赖项、更快和更少的编译)。
Second, you can just define the template function in the .cpp and explicitly instantiate it for all the types it'll be ever used with.
其次,您可以只在 .cpp 中定义模板函数,并为它将使用的所有类型显式实例化它。
For example, this can go in the end of the .cpp to make the function usable with int
s:
例如,这可以放在 .cpp 的末尾,使函数可以与int
s 一起使用:
template void Foo::RemoveVectorDuplicates(std::vector<int>*);
However, this assumes you only use templates to save some typing, rather than to provide true genericity.
但是,这假设您仅使用模板来保存一些输入,而不是提供真正的通用性。
回答by j_random_hacker
One alternative you have is to first std::sort()
the vector, and then use the pre-existing std::unique()
function to remove duplicates. The sort takes O(nlog n) time, and removing duplicates after that takes just O(n) time as all duplicates appear in a single block. Your current "all-vs-all" comparison algorithm takes O(n^2) time.
一种替代方法是首先std::sort()
使用向量,然后使用预先存在的std::unique()
函数删除重复项。排序需要 O(nlog n) 时间,然后删除重复项只需要 O(n) 时间,因为所有重复项都出现在单个块中。您当前的“all-vs-all”比较算法需要 O(n^2) 时间。
回答by jalf
You can't implement a template function in a .cpp file. The complete implementation has to be visible anywhere it's instantiated.
您不能在 .cpp 文件中实现模板函数。完整的实现必须在它被实例化的任何地方可见。
Just define the function inside the class definition in the header. That's the usual way to implement template functions.
只需在标题中的类定义中定义函数即可。这是实现模板函数的常用方法。
回答by Ismael
I'll suggest to use a more "generic" approach, instead of passing a container just receive two iterators.
我建议使用更“通用”的方法,而不是传递一个容器,只接收两个迭代器。
Something like It remove_duplicates(It first, It last), and will return an iterator, so you can call like remove: v.erase(remove_duplicates(v.begin(), v.end()), v.end())
.
像它 remove_duplicates(It first, It last) 之类的东西,并且会返回一个迭代器,所以你可以像 remove: 一样调用v.erase(remove_duplicates(v.begin(), v.end()), v.end())
。
template <typename It>
It remove_duplicate(It first, It last)
{
It current = first;
while(current != last) {
// Remove *current from [current+1,last)
It next = current;
++next;
last = std::remove(next, last, *current);
current = next;
}
return last;
}
回答by Konrad Rudolph
Unrelated to your problem (which has been explained already), why is this a static function rather than residing globally in a namespace? This would be somewhat C++-ier.
与您的问题无关(已经解释过),为什么这是一个静态函数而不是全局驻留在命名空间中?这有点像 C++。
回答by Hippiehunter
I don't think that code compiles....
我不认为代码编译....
vectorToUpdate.erase where std::vector* vectorToUpdate.... does anyone else notice there is a * where there should be a &? that code is definitely not being compiled. if you're going to use a pointer to vector you must use '->' instead of '.' I know this is actually a bit nit picky but it points out that the compiler doesn't even care about your code...
vectorToUpdate.erase where std::vector* vectorToUpdate.... 有没有其他人注意到有一个 * 应该有一个 &?该代码绝对没有被编译。如果要使用指向向量的指针,则必须使用“->”而不是“.”。我知道这实际上有点挑剔,但它指出编译器甚至不关心你的代码......