简体   繁体   English

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

[英]How do you create a static template member function that performs actions on a template class?

I'm trying to create a generic function that removes duplicates from an std::vector. 我正在尝试创建一个通用函数,从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: 这是我有的:

//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. 更新:基于Iraimbilanja给出的答案,我去重写了代码。 However, just in case someone wanted working code to do the RemoveDuplicates function, here it is: 但是,万一有人想要使用代码来执行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. 事实证明,如果我在签名中指定std :: vector,则迭代器无法正常工作。 So I had to go with a more generic approach. 所以我不得不采用更通用的方法。 Also, when erasing compareIter, the next iteration of the loop produces a pointer exception. 此外,当擦除compareIter时,循环的下一次迭代会产生指针异常。 The post decrement of compareIter on an erase takes care of that problem. 擦除时compareIter的后递减处理了该问题。 I also fixed the bugs in the iterator compare and in the initialization of compareIter in the 2nd loop. 我还修复了迭代器比较中的错误以及第二个循环中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. 我看到这个问题得到了另一个投票,所以我想用一个更好的算法更新它,使用一些C ++ 14的优点。 My previous one only worked if the type stored in the vector implemented operator== and it required a bunch of copies and unnecessary comparisons. 我之前的一个只有在存储在向量中的类型实现了operator ==并且它需要一堆副本和不必要的比较时才有效。 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. 该名称已更改为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));
}

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 d转换为任何翻译单元:它只能用于它定义的翻译单元。

Hence RemoveVectorDuplicates must be defined in the header, as this is the only way the compiler can text-substitute the template arguments, hence instantiating the 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 end of the header : 首先 ,您可以从.cpp中删除#include "foo.h"并在标题末尾添加另一个: #include "foo.h"

#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

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

However, this assumes you only use templates to save some typing, rather than to provide true genericity. 但是,这假设您只使用模板来保存一些输入,而不是提供真正的通用性。

One alternative you have is to first std::sort() the vector, and then use the pre-existing std::unique() function to remove duplicates. 你有一个替代方法是首先使用std::sort()向量,然后使用预先存在的std::unique()函数来删除重复项。 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. 排序需要O(nlog n)时间,并且在此之后删除重复项只需要O(n)时间,因为所有重复项都出现在单个块中。 Your current "all-vs-all" comparison algorithm takes O(n^2) time. 您当前的“全部与全部”比较算法需要O(n ^ 2)时间。

You can't implement a template function in a .cpp file. 您无法在.cpp文件中实现模板功能。 The complete implementation has to be visible anywhere it's instantiated. 完整的实现必须在实例化的任何地方都可见。

Just define the function inside the class definition in the header. 只需在标题中的类定义中定义函数即可。 That's the usual way to implement template functions. 这是实现模板功能的常用方法。

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(它首先,它是最后一个),并将返回一个迭代器,所以你可以调用像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;
}

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 ++ - ier。

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 &? vectorToUpdate.erase其中std :: vector * vectorToUpdate ....其他人都注意到有一个*应该有一个&? 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... 我知道这实际上有点挑剔,但它指出编译器甚至不关心你的代码......

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM