0

I'm a CS student, and the I have a task where I need some help. The task is to create a vectors_predicate_view class that gets a T type as the first template parameter and a predicate as the second template parameter. In its constructor the class should get 2 std::vectors as parameters.

Here is the class:

#pragma once
#include <vector>

template<typename T, typename Predicate>
class vectors_predicate_view 
{
private:
    Predicate predict;
    std::vector<T> originalContents1;
    std::vector<T> originalContents2;

    std::vector<T> &original1; // reference to original vector1
    std::vector<T> &original2; // reference to original vector2

    void setElements(std::vector<T> &vector1, std::vector<T> &vector2)
    {
        //Putting every element from vector1 into originalContents1
        for(auto element : vector1)
            originalContents1.push_back(element);
        vector1.clear();    //Emptying vector1

        //Putting every element from vector2 into originalContents2
        for(auto element : vector2)
            originalContents2.push_back(element);
        vector2.clear();    //Emptying vector2

        //We loop through originalContents1
        //if the element gives back true for the predictatum, we add it to vector1
        //else it goes to vector2
        for(auto element : originalContents1) 
        {
            if (predict(element))
                vector1.push_back(element);
            else
                vector2.push_back(element);
        }

        //We loop through originalContents2
        //if the element gives back true for the predictatum, we add it to vector1
        for(auto element : originalContents2)
            if(predict(element))
                vector1.push_back(element);
    }

public:
    vectors_predicate_view(std::vector<T> &vector1, std::vector<T> &vector2) : original1(vector1), original2(vector2)
    {
        setElements(vector1, vector2);
    }
};

The task is to make the second parameter optional, and if it's not given, the class should not do anything with the 2 given vectors inside its constructor.

Just to give an idea, here is how that second template optimally looks like. It's an std::unary_function that looks like this:

struct is_even: std::unary_function<int, bool>
{
    bool operator()(int i) const
    {
        return 0 == i % 2;
    }
};

struct is_good_language: std::unary_function<std::string, bool>
{
    bool operator()(const std::string& s) const
    {
        return s == "C++" || s == "C";
    }
};

and called like this:

const vectors_predicate_view<int, is_even> va(a, b);
vectors_predicate_view<std::string, is_good_language> vb(x, y);

Any help would be greatly appreciated!

L. F.
  • 19,445
  • 8
  • 48
  • 82
  • Are you supposed to modify the vectors passed to the constructor, or create new vectors inside the view? Also, what you need might be simply a predicate that always returns true. – L. F. Dec 20 '20 at 10:57
  • 1
    @L.F. Both. I have to change the vectors passed to the constructor and copy their original state inside the class, into two new vectors, so when the destructor is called, the two vectors can be reset to their original state, based on that 2 new vectors that I created inside the class. How can I determine if that predicate that I created and always returns true is not the one that's passed through the second template? – Martin Serdült Dec 20 '20 at 11:17

1 Answers1

1

You can define a predicate that always returns true no matter what the argument is:

struct always_true {
    template <typename T>
    constexpr bool operator()(T&&) const noexcept {
        return true;
    }
};

Then, set this predicate to be the default argument for the template parameter Predicate:

template <typename T, typename Predicate = always_true>
                                      // ^^^^^^^^^^^^^ default argument
class vectors_predicate_view {
    // ...
};

When the user explicitly supplies an argument, the default argument is ignored.


To differentiate the default case from user-supplies predicates, an alternative approach is to put the tag in a detail namespace — which is private to the implementation, by convention — and use specialization to handle the case:

namespace detail {
    struct tag {};
}

template <typename T, typename Predicate = detail::tag>
                                      // ^^^^^ default argument
class vectors_predicate_view {
    // normal case
};

template <typename T>
class vectors_predicate_view<T, detail::tag> {
    // default case
};

When the user does not provide the second template argument, the specialization is used. This may cause some code duplication, but it gives you full control over what to do in the default case.


Aside: since C++11, there is no need to derive from unary_function, etc. — see Why have unary_function, binary_function been removed from C++11?.

L. F.
  • 19,445
  • 8
  • 48
  • 82
  • Thank you for your help! :D How can I determine if that predicate that I created and always returns true is not the one that's passed through the second template? Because if it's the one, which we created then we should not do anything inside the vectors_predicate_view class – Martin Serdült Dec 20 '20 at 11:44
  • @MartinSerdült You can put the predicate in a "detail" namespace (which is private to the implementation by convention) and add a specialization after the primary template: `template class vectors_predicate_view`. – L. F. Dec 20 '20 at 11:47
  • Ok, i kinda lost you there :'D So I create a namespace named "detail", but what do you mean by adding a specialization after the primary template: `template class vectors_predicate_view`? – Martin Serdült Dec 20 '20 at 12:06
  • 1
    @MartinSerdült I've updated my answer to hopefully clarify. – L. F. Dec 20 '20 at 12:10