C++ 如何创建对模板类执行操作的静态模板成员函数?

C++ 如何创建对模板类执行操作的静态模板成员函数?,c++,stl,templates,vector,static-members,C++,Stl,Templates,Vector,Static Members,我试图创建一个通用函数,用于从std::vector中删除重复项。因为我不想为每种向量类型创建一个函数,所以我想让它成为一个可以接受任何类型向量的模板函数。以下是我所拥有的: //foo.h Class Foo { template<typename T> static void RemoveVectorDuplicates(std::vector<T>& vectorToUpdate); }; //foo.cpp template<typenam

我试图创建一个通用函数,用于从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);
}

无法在.cpp文件中实现模板函数。完整的实现必须在实例化的任何地方都可见

只需在标题中的类定义中定义函数。 这是实现模板功能的常用方法。

简短回答 在标题中定义函数,最好在类定义中定义

长话短说 在.cpp中定义模板函数意味着它不会将
#include
d包含到任何翻译单元中:它将仅对定义它的翻译单元可用

因此,必须在标头中定义
RemoveVectorDuplicates
,因为这是编译器可以用文本替换模板参数的唯一方法,从而实例化模板,生成可用类

对于这种不便,有两种解决办法 首先,您可以从.cpp中删除
#include“foo.h”
,并在标题末尾添加另一个:

这使您能够一致地组织文件,但并没有提供单独编译的通常优势(较小的依赖项、更快和更少的编译)

其次,您只需在.cpp中定义模板函数,并显式实例化它将用于的所有类型

例如,这可以放在.cpp的末尾,使函数可用于
int
s:

template void Foo::RemoveVectorDuplicates(std::vector<int>*);
模板void Foo::RemoveVectorDuplicates(std::vector*);

但是,这假设您只使用模板来保存某些类型,而不是提供真正的泛型性。

与您的问题无关(已经解释过),为什么这是一个静态函数而不是全局驻留在命名空间中?这有点像C++-ier。

我不认为代码可以编译


vectorToUpdate.erase其中std::vector*vectorToUpdate。。。。有没有人注意到有一个*应该有一个&?这段代码肯定没有被编译。如果要使用指向向量的指针,则必须使用“->”而不是“.”。我知道这实际上有点吹毛求疵,但它指出编译器甚至不关心您的代码…

我建议使用更“通用”的方法,而不是只传递一个容器和接收两个迭代器

类似于它的东西会删除重复项(它是第一个,它是最后一个),并返回一个迭代器,因此您可以调用类似于remove:
v.erase(删除重复项(v.begin(),v.end()),v.end())

模板
删除重复项(先删除,后删除)
{
它电流=第一;
while(当前!=上次){
//从[current+1,last]中删除*电流
它下一个=当前;
++其次;
最后一个=标准::删除(下一个,最后一个,*当前);
当前=下一个;
}
最后返回;
}

一种替代方法是首先
std::sort()
向量,然后使用预先存在的
std::unique()
函数删除重复项。排序需要O(nlog n)时间,之后删除重复项只需要O(n)时间,因为所有重复项都显示在单个块中。您当前的“all vs all”比较算法需要O(n^2)时间。

将定义与声明放在一起,即将函数体移到标题中。不久会有人发布有关导出的内容,但基本上编译器在看到用类型实例化的模板之前不会生成代码。或者类似的内容。您的实现不安全,因为您正在修改容器保持迭代器在for循环内使用。迭代器不需要知道对容器的修改(出于性能原因),如果某些修改导致容器释放/分配内存,迭代器可能会指向无效内存。@xhantt:vector::erase()承诺保留序列中早期元素的所有迭代器不变,因此前提是它被称为vectorToUpdate.erase(compareIter--)您是安全的。我可能已经完成了compareIter=vectorToUpdate.erase(compareIter);因为这可能更直截了当一些。你是对的,我从源文件中复制错了,将*改为&@bsruth:你还需要去掉对Foo::RemoveVectorDuplicates()的调用中的“&”在底部。这与从遗留代码库迁移有关。最终,我计划按照您的建议进行操作。您能否详细说明您的答案,解释为什么在保留迭代器的同时修改容器是不安全的?+1用于避免O(n^2)个副本的解决方案(即使总体运行时间仍然是O(n^2))。(请注意,与此相反,OP解决方案中对erase()的每次调用都会将所有后续对象向下移动一个位置。)不正确。参见/ /虽然,对C++的支持在整个C++中都是不同的。接受的答案描述了官方的方法:告诉C++编译器哪些实例化要创建。一些编译器可能不需要。这是我自己的拙见。legant@bsruth,你应该简单地连续调用这两个函数。你可以用一个内联方法将其封装起来,使其整洁。这是一个很好的答案,它最终回答了这个(以及相关的)问题,除了一个解决方法之外,还提供了一些解决方法,因此这可以应用于其他场景。
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));
}
#include "foo.cpp"
template void Foo::RemoveVectorDuplicates(std::vector<int>*);
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;
}