简体   繁体   中英

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

I defined a class FileList which do perform create a list of files which meet the regular expression( f_regex argument).

To sort the list, an instance of SortBy*** struct( inherited from FileSorter ) can be passed.

The problem is std::sort function cannot be compiled with the code above showing the following error message.

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

I don't understand this behavior. In my narrow knowledge, struct equipped with operator () is called functor and it is a good way to deal with a function as an object.

And as all we know, instance of child class can be referred by a reference of parent class .

But the above example is saying differently.

What should I change to make the code work?

If I have wrong concepts about c++, please don't hesitate to scold me.

Full compile error messages are here.

$ 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

If you look at the signature, std::sort takes its comparison object by value :

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

So when you write:

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

your object gets sliced, and you end up trying to instantiate a function which takes an abstract class by value, hence all the errors you end up with.

What you need to do is ensure that even though sort takes its comparison by value, you pass yours in by reference. Thankfully, there's an app for that! Just use std::ref :

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

That said, do you really need a polymorphic comparator? If you're just passing in different comparison function objects into the FilesList constructor, you should prefer to just make it a function template:

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

That way, you can just directly forward in what the user passes and avoid virtual dispatch.

Your FileSorter argument is defaulted to a SortByExtension object rather than a SortByName object. As you haven't included the source of SortByExtension, I would start by checking that function signature of SortByExtension's function call operator is

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

If there are any differences between the base class and derived class function signatures, the derived class function won't override the base class one, and the derived class will be treated as an abstract class.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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