使用 std::sort 时禁止仿函数(继承)?

A functor(inherrited) is forbidden when using std::sort?

#include <string.h>
#include <vector>
#include <algorithm>
#include <boost/filesystem.hpp>
#include <boost/foreach.hpp>
#include <boost/regex.hpp>

struct FileSorter{

virtual ~FileSorter(){}
    virtual bool operator()(const boost::filesystem::path& p1, const boost::filesystem::path& p2) const =0;
};

struct SortByName : public FileSorter{
    SortByName(bool ascending=true):ascending_order(ascending)
    {
    }

    virtual bool operator()(const boost::filesystem::path& p1, const boost::filesystem::path& p2) const {
        if(ascending_order)
            return p1.stem().string() < p2.stem().string();
        else
            return p1.stem().string() > p2.stem().string();
    }

protected:
    bool ascending_order;
};

class FilesList : public std::vector<boost::filesystem::path> {
public:
    FilesList(const std::string& dir, const std::string& f_regex, const FileSorter& fileSorter=SortByName()) {
        boost::regex e(f_regex, boost::regex::perl);
        boost::filesystem::path path(dir);
        if(!boost::filesystem::is_directory(path)) {
            throw std::runtime_error(path.string()+std::string(" is not a directory\n"));
        }

        for(boost::filesystem::directory_iterator file(path), f_end; file!= f_end; ++file){
            if(boost::regex_match(file->path().filename().string(), e))
                this->push_back(file->path());
        }

        std::sort(this->begin(), this->end(), fileSorter);
    }
};

我定义了一个 class FileList 来执行创建满足正则表达式(f_regex 参数)的文件列表。

要对列表进行排序,可以传递 SortBy*** struct(inherited from FileSorter) 的实例。

问题是 std::sort 函数无法用上面的代码编译,显示以下错误消息。

/usr/include/c++/4.8/bits/stl_algo.h:5483:5: error: cannot allocate an object of abstract type ‘FileSorter’

我不明白这种行为。在我狭隘的知识范围内,带有 operator () 的结构称为 functor ,这是将函数作为对象处理的好方法。

正如我们所知,child class 的实例可以通过 parent class[=42 的引用来引用=].

但上面的例子是不同的说法。

我应该更改什么才能使代码正常工作?

如果我对c++的概念有误,欢迎大家批评指正

完整的编译错误信息在这里。

$ make
Scanning dependencies of target cpp_factory
[ 11%] Building CXX object CMakeFiles/cpp_factory.dir/libraries/src/FileLister.cpp.o
In file included from /home/ub1404/Application/cpp_factory/libraries/src/FileLister.cpp:5:0:
/home/ub1404/Application/cpp_factory/libraries/include/cpp_factory/files/FileLister.h: In constructor ‘FilesList::FilesList(const string&, const string&, const FileSorter&)’:
/home/ub1404/Application/cpp_factory/libraries/include/cpp_factory/files/FileLister.h:109:57: error: no matching function for call to ‘sort(std::vector<boost::filesystem::path>::iterator, std::vector<boost::filesystem::path>::iterator, const FileSorter&)’
         std::sort(this->begin(), this->end(), fileSorter);
                                                         ^
/home/ub1404/Application/cpp_factory/libraries/include/cpp_factory/files/FileLister.h:109:57: note: candidates are:
In file included from /usr/include/c++/4.8/algorithm:62:0,
                 from /home/ub1404/Application/cpp_factory/libraries/include/cpp_factory/files/FileLister.h:11,
                 from /home/ub1404/Application/cpp_factory/libraries/src/FileLister.cpp:5:
/usr/include/c++/4.8/bits/stl_algo.h:5447:5: note: template<class _RAIter> void std::sort(_RAIter, _RAIter)
     sort(_RandomAccessIterator __first, _RandomAccessIterator __last)
     ^
/usr/include/c++/4.8/bits/stl_algo.h:5447:5: note:   template argument deduction/substitution failed:
In file included from /home/ub1404/Application/cpp_factory/libraries/src/FileLister.cpp:5:0:
/home/ub1404/Application/cpp_factory/libraries/include/cpp_factory/files/FileLister.h:109:57: note:   candidate expects 2 arguments, 3 provided
         std::sort(this->begin(), this->end(), fileSorter);
                                                         ^
In file included from /usr/include/c++/4.8/algorithm:62:0,
                 from /home/ub1404/Application/cpp_factory/libraries/include/cpp_factory/files/FileLister.h:11,
                 from /home/ub1404/Application/cpp_factory/libraries/src/FileLister.cpp:5:
/usr/include/c++/4.8/bits/stl_algo.h:5483:5: note: template<class _RAIter, class _Compare> void std::sort(_RAIter, _RAIter, _Compare)
     sort(_RandomAccessIterator __first, _RandomAccessIterator __last,
     ^
/usr/include/c++/4.8/bits/stl_algo.h:5483:5: note:   template argument deduction/substitution failed:
/usr/include/c++/4.8/bits/stl_algo.h: In substitution of ‘template<class _RAIter, class _Compare> void std::sort(_RAIter, _RAIter, _Compare) [with _RAIter = __gnu_cxx::__normal_iterator<boost::filesystem::path*, std::vector<boost::filesystem::path> >; _Compare = FileSorter]’:
/home/ub1404/Application/cpp_factory/libraries/include/cpp_factory/files/FileLister.h:109:57:   required from here
/usr/include/c++/4.8/bits/stl_algo.h:5483:5: error: cannot allocate an object of abstract type ‘FileSorter’
In file included from /home/ub1404/Application/cpp_factory/libraries/src/FileLister.cpp:5:0:
/home/ub1404/Application/cpp_factory/libraries/include/cpp_factory/files/FileLister.h:49:8: note:   because the following virtual functions are pure within ‘FileSorter’:
 struct FileSorter{
        ^
/home/ub1404/Application/cpp_factory/libraries/include/cpp_factory/files/FileLister.h:51:18: note:  virtual bool FileSorter::operator()(const boost::filesystem::path&, const boost::filesystem::path&) const
     virtual bool operator()(const boost::filesystem::path& p1, const boost::filesystem::path& p2) const =0;
                  ^
make[2]: *** [CMakeFiles/cpp_factory.dir/libraries/src/FileLister.cpp.o] Error 1
make[1]: *** [CMakeFiles/cpp_factory.dir/all] Error 2
make: *** [all] Error 2

如果您查看签名,std::sort 将其比较对象按值:

template< class RandomIt, class Compare >
void sort( RandomIt first, RandomIt last, Compare comp );

所以当你写的时候:

std::sort(this->begin(), this->end(), fileSorter);

你的对象被切片,你最终试图实例化一个函数,它接受一个抽象的 class 值,因此你最终会遇到所有错误。

您需要做的是确保即使 sort 按值进行比较,您也通过引用传递您的值。值得庆幸的是,有一个应用程序!只需使用 std::ref:

std::sort(this->begin(), this->end(), std::ref(fileSorter));

也就是说,您真的需要多态比较器吗?如果您只是将不同的比较函数对象传递给 FilesList 构造函数,您应该更愿意将其设为函数模板:

template <class Sorter>
FilesList(const std::string& dir, const std::string& f_regex, Sorter fileSorter) {
    // ...
    std::sort(begin(), end(), fileSorter); // now copying is fine
}

这样,你可以直接转发用户传递的内容,避免虚拟调度。

您的 FileSorter 参数默认为 SortByExtension 对象而不是 SortByName 对象。由于您没有包括 SortByExtension 的来源,我首先检查 SortByExtension 的函数调用运算符的函数签名是

bool SortByExtension::operator()(const path&, const path&) const

如果基本 class 和派生 class 函数签名之间存在任何差异,派生 class 函数将不会覆盖基本 class 函数,并且派生的 class 将被视为抽象 class.