2
votes
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;
}

When compiling the aforementioned code g++ prints this to the console:

/tmp/ccblK6El.o: In function HowMuch<std::__cxx11::list<int, std::allocator<int> >, int>::operator()(std::__cxx11::list<int, std::allocator<int> > const&) const: templates.cpp:(.text._ZNK7HowMuchINSt7__cxx114listIiSaIiEEEiEclERKS3_[_ZNK7HowMuchINSt7__cxx114listIiSaIiEEEiEclERKS3_]+0x84): undefined reference to Predicate<int>::operator()(int const&) const collect2: error: ld returned 1 exit status The terminal process terminated with exit code: 1

I can't quite get what is the problem with Prediate<V> definition inside HowMuch, because for me (a newbie in C++) it really looks LGTM. From my understanding the compiler creates a definition of Predicate<int> as a separate type, and the logs say exactly that, but for some reason it can't find the typed definition of the overloaded invocation operator. May be the problem is with the type deduction? Template of a container template type itself has to be somehow explicitly defined?

EDIT:

virtual modifier was added to both Predicate's and Producer's function operator overloads, but the problem, as it seems, persists. The error "description" (if it can be called as helpful description) is changed a bit, however (but still it points to the same problem):

/tmp/ccn1Swqa.o: In function HowMuch >, int>::operator()(std::__cxx11::list > const&) const: templates.cpp:(.text._ZNK7HowMuchINSt7__cxx114listIiSaIiEEEiEclERKS3_[_ZNK7HowMuchINSt7__cxx114listIiSaIiEEEiEclERKS3_]+0x76): undefined reference to Predicate::operator()(int const&) const /tmp/ccn1Swqa.o:(.rodata._ZTV8ProducerINSt7__cxx114listIiSaIiEEEiE[_ZTV8ProducerINSt7__cxx114listIiSaIiEEEiE]+0x10): undefined reference to Producer >, int>::operator()(std::__cxx11::list > const&) const /tmp/ccn1Swqa.o:(.rodata._ZTV9PredicateIiE[_ZTV9PredicateIiE]+0x10): undefined reference to Predicate::operator()(int const&) const collect2: error: ld returned 1 exit status The terminal process terminated with exit code: 1

3
It seems you want C++20 concepts, instead of this unneeded inheritance. - Jarod42
@Jarod42 I'm learning C++ as a support language, not more, so jumping from Stroustrup's C++11 book straight to c++20 standard seems inadequate, especially when the problem is already resolved - nyarian
Which is the purpose of Predicate? Seems unneeded. Indeed current answer fix compilation issue, but base class seems useless here. - Jarod42
@Jarod42 this code obviously serves no purpose aside of practicing of features that I'm getting from the book, whereas here I just wanted to create a Java 8 port of functional interfaces hierarchy to see if it works as expected. And how does it relate to the original question at all? I won't stop you from making this code snippet ideal however, whatever the purpose is :) - nyarian
The Java way to solve a issue is not necessary the C++ way (as for this case) - Jarod42

3 Answers

3
votes

You need to provide definitions for all functions of your classes. That means even if you only derive classes from Predicate and Producer you still have to implement the operator() in those classes.

If you don't want to do that (i.e. only have the function declaration but no definition), consider making these two classes abstract by declaring the operator() methods pure virtual. Then you cannot instantiate an object directly from these classes but only from derived classes that implement the operator() method. And that also means you can only pass Predicate<V>* in your HowMuch constructor.

2
votes

If you want Predicate to be an abstract class, you need to:

-make the bool operator() method virtual and set it to 0 (in Predicate class):

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

-store a reference to the Predicate in HowMuch:

explicit HowMuch(Predicate<V> &p) : predicate{p} {}
Predicate<V> &predicate;
1
votes

Inheritance seems not justified in your case, you might get rid of your base classes:

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

If you really want base class as interface, you need pure virtual call

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

You also need to fix the object slicing you have:

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