C ++: неопределенная ссылка на перегруженный оператор вызова функтора - PullRequest
2 голосов
/ 28 мая 2020
template <typename T>
class Predicate {
   public:
    bool operator()(const T& x) const;
};

template <typename T>
class LessThan : public Predicate<T> {
   public:
    explicit LessThan(const T& v) : val(v) {}
    bool operator()(const T& x) const { return x < val; }

   private:
    const T val;
};

template <typename C, typename T>
class Producer {
   public:
    T operator()(const C& c) const;
};

template <typename C, typename V>
class HowMuch : public Producer<C, int> {
   public:
    explicit HowMuch(Predicate<V> p) : predicate{p} {}
    int operator()(const C& c) const {
        int count = 0;
        for (const auto& x : c)
            if (predicate(x)) ++count;
        return count;
    }

   private:
    Predicate<V> predicate;
};

int main() {
    const LessThan<int> lf(5);
    const HowMuch<list<int>, int> hm(lf);
    list<int> li {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    cout << "How much numbers less than 5 is in {1, 2, 3, 4, 5, 6, 7, 8, 9, "
            "10}? Answer: "
         << hm(li)
         << endl;
}

При компиляции вышеупомянутого кода g ++ выводит это на консоль:

/ tmp / ccblK6El.o: В функции HowMuch<std::__cxx11::list<int, std::allocator<int> >, int>::operator()(std::__cxx11::list<int, std::allocator<int> > const&) const: шаблоны. cpp :(. text._ZNK7HowMuchINSt7__cxx114listIiSaIiEEEiEclERKS3 _ [_ ZNK7HowMuchINSt7__cxx114listIiSaIiEEEiEclERKS3 _] + 0x84): неопределенная ссылка на Predicate<int>::operator()(int const&) const завершенный процесс 1011 * завершен 1009 с ошибкой: l Я вполне понимаю, в чем проблема с определением Prediate<V> внутри HowMuch, потому что для меня (новинка ie в C ++) это действительно выглядит LGTM. Насколько я понимаю, компилятор создает определение Predicate<int> как отдельный тип, и журналы говорят именно это, но по какой-то причине он не может найти типизированное определение перегруженного оператора вызова. Может проблема в выводе типа? Шаблон самого типа шаблона контейнера должен быть каким-то образом явно определен?

EDIT: модификатор

virtual был добавлен к перегрузкам операторов функций Predicate и Producer , но проблема, похоже, сохраняется. Однако "описание" ошибки (если его можно назвать полезным описанием) немного изменено (но все же указывает на ту же проблему):

/ tmp / ccn1Swqa.o: In function HowMuch>, int> :: operator () (std :: __ cxx11 :: list> const &) const: templates. cpp :(. Text._ZNK7HowMuchINSt7__cxx114listIiSaIiEEEiEclERKS3 _ [_ ZNK7HowMuchINSt7__Cxx114listI) для ссылки на оператор_Cxx114ListI_E_CXXA_CXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXI (int const &) const /tmp/ccn1Swqa.o:(.rodata._ZTV8ProducerINSt7__cxx114listIiSaIiEEEiE[_ZTV8ProducerINSt7__cxx114listIiSaIiEEEiE visible+0x10): undefined ссылка на Producer :: const__>, int> :: tmp / ccn1Swqa.o :(. rodata._ZTV9PredicateIiE [_ZTV9PredicateIiE] + 0x10): неопределенная ссылка на Predicate :: operator () (int const &) const collect2: error: ld вернул 1 статус выхода. Терминальный процесс завершился с кодом выхода: 1

Ответы [ 3 ]

2 голосов
/ 28 мая 2020

Если вы хотите, чтобы Predicate был абстрактным классом, вам необходимо:

-сделать метод bool operator () виртуальным и установить для него значение 0 (в классе Predicate):

virtual bool operator()(const T& x) const=0;

- сохранить ссылку на предикат в HowMuch:

explicit HowMuch(Predicate<V> &p) : predicate{p} {}
Predicate<V> &predicate;
2 голосов
/ 28 мая 2020

Вам необходимо предоставить определения для всех функций ваших классов. Это означает, что даже если вы наследуете классы только от Predicate и Producer, вам все равно придется реализовать operator() в этих классах.

Если вы не хотите этого делать (т.е. иметь только функцию декларации, но без определения), рассмотрите возможность сделать эти два класса абстрактными , объявив operator() методы чисто виртуальные . Тогда вы не сможете создать экземпляр объекта непосредственно из этих классов, а только из производных классов, реализующих метод operator(). И это также означает, что вы можете передать только Predicate<V>* в свой конструктор HowMuch.

1 голос
/ 28 мая 2020

Наследование в вашем случае кажется неоправданным, вы можете избавиться от своих базовых классов:

template <typename T>
class LessThan {
public:
    explicit LessThan(const T& v) : val(v) {}
    bool operator()(const T& x) const { return x < val; }

private:
    T val;
};

template <typename Predicate>
class HowMuch {
   public:
    explicit HowMuch(Predicate p) : predicate{p} {}

    template <typename Container>
    int operator()(const Container& c) const {
        int count = 0;
        for (const auto& x : c)
            if (predicate(x)) ++count;
        return count;
    }

private:
    Predicate predicate;
};

int main() {
    const LessThan<int> lf(5);
    const HowMuch<LessThan<int>> hm(lf);
    std::list<int> li {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    std::cout << "How much numbers less than 5 is in {1, 2, 3, 4, 5, 6, 7, 8, 9, "
            "10}? Answer: "
         << hm(li)
         << std::endl;
}

Demo

Если вам действительно нужен базовый класс в качестве интерфейса , вам нужен чистый виртуальный вызов

template <typename T>
class Predicate {
public:
    virtual ~Predicate() = default;
    virtual bool operator()(const T& x) const = 0;
};

template <typename C, typename T>
class Producer {
public:
    virtual ~Producer() = default;
    virtual T operator()(const C& c) const = 0;
};

Также необходимо исправить имеющуюся нарезку объекта:

template <typename C, typename V>
class HowMuch : public Producer<C, int> {
   public:
    explicit HowMuch(const Predicate<V>& p) : predicate{&p} {}
    int operator()(const C& c) const override {
        int count = 0;
        for (const auto& x : c)
            if ((*predicate)(x)) ++count;
        return count;
    }

private:
    const Predicate<V>* predicate;
};

Demo

...