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