如何创建对模板类执行操作的静态模板成员函数? [英] How do you create a static template member function that performs actions on a template class?

查看:136
本文介绍了如何创建对模板类执行操作的静态模板成员函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

#includefoo.h

...

void SomeOtherClass :: SomeFunction(void){
std :: vector< int> myVector;

//用值填充向量

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 );
}
}
}
};

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



UPDATE 2:



我看到这个问题得到了另一个投票,所以我想更新它与更好的算法,使用一些C ++ 14好。我以前的一个只工作,如果类型存储在向量实现运算符==和它需要一堆副本和不必要的比较。而且,事后看来,没有必要让它成为一个类的成员。这种新算法允许自定义比较谓词,收缩比较空间,因为发现重复,并且显着减少了副本数量。名称已更改为 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;
}
containersToUpdate.erase(firstDuplicateIter,end(containerToUpdate));
}


解决方案



在标题中定义函数,最好在类定义中。



长回答


b $ b

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



因此 RemoveVectorDuplicates 必须在头文件中定义,因为这是编译器唯一的方法



这种不便有两种解决方法



首先,您可以从.cpp中删除 #includefoo.h 结尾

  #include foo.cpp

这样可以一致地整理文件,



第二,您只需在.cpp中定义模板函数,并显式地



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

  int> *); 

然而,这假设你只使用模板来保存一些打字,而不是提供真正的一般性。 / p>

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:

//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:

//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.

UPDATE 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.

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

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.

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:

#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.

For example, this can go in the end of the .cpp to make the function usable with 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.

这篇关于如何创建对模板类执行操作的静态模板成员函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆