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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 15:38:10  来源:igfitidea点击:

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

c++stltemplatesvectorstatic-members

提问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_duplicatesto 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 #included into any translation units: it will only be available to the translation unit it's defined in.

在 .cpp 中定义模板函数意味着它不会#include进入任何翻译单元:它只对定义它的翻译单元可用。

Hence RemoveVectorDuplicatesmust 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 ints:

例如,这可以放在 .cpp 的末尾,使函数可以与ints 一起使用:

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.... 有没有其他人注意到有一个 * 应该有一个 &?该代码绝对没有被编译。如果要使用指向向量的指针,则必须使用“->”而不是“.”。我知道这实际上有点挑剔,但它指出编译器甚至不关心你的代码......