繁体   English   中英

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

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

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

我一直收到链接器错误,但它编译得很好。 关于我做错了什么的任何想法?

更新:基于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);
            }
        }
    }
};

事实证明,如果我在签名中指定std :: vector,则迭代器无法正常工作。 所以我不得不采用更通用的方法。 此外,当擦除compareIter时,循环的下一次迭代会产生指针异常。 擦除时compareIter的后递减处理了该问题。 我还修复了迭代器比较中的错误以及第二个循环中compareIter的初始化。

更新2:

我看到这个问题得到了另一个投票,所以我想用一个更好的算法更新它,使用一些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));
}

简答

在头文件中定义函数,最好在类定义中。

答案很长

在.cpp中定义模板函数意味着它不会将#include d转换为任何翻译单元:它只能用于它定义的翻译单元。

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

这种不便有两种解决方法

首先 ,您可以从.cpp中删除#include "foo.h"并在标题末尾添加另一个: #include "foo.h"

#include "foo.cpp"

这使您可以一致地组织文件,但不提供单独编译的常见优势(较小的依赖性,更快和更罕见的编译)。

其次 ,您可以在.cpp中定义模板函数,并为它将要使用的所有类型显式实例化它。

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

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

但是,这假设您只使用模板来保存一些输入,而不是提供真正的通用性。

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

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

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

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

类似于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;
}

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

我不认为代码编译....

vectorToUpdate.erase其中std :: vector * vectorToUpdate ....其他人都注意到有一个*应该有一个&? 该代码绝对没有被编译。 如果您要使用指向矢量的指针,则必须使用' - >'而不是'。' 我知道这实际上有点挑剔,但它指出编译器甚至不关心你的代码......

暂无
暂无

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

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