c++c++20class-templatelifting

Lifting member functions from a class template to a second class template over the first one


This is a follow-up question to this question.

There, thanks to the answer of Jarod42, I managed to lift the member function get_first()of a class template two_val<T> to all classes of the form trait_vector<two_val<T>>. See the code block below in order to see what these classes are and how the member function lift was achieved.

template<class T>
class two_val{
    private:   
        T first_val;
        T second_val;
               
    public:
        using type = T;

        two_val() :
            first_val {},
            second_val {}
        {}

        T get_first(){return first_val;}        
};

template <typename T> struct is_two_val : std::false_type {};
template <typename T> struct is_two_val<two_val<T>> : std::true_type {};

template<class T>
class trait_vector{
private: 
    std::vector<T> vec;
                
public:
    trait_vector() : vec {} {}
        
    auto get_first() requires (is_two_val<T>::value)
    {
        trait_vector<typename T::type> result{};
        result.reserve(vec.size());
        for(const auto& p : vec){
            result.vec.push_back(p.get_first());
        }
        return result;
    }
};

This all works well and is very nice.

But now I want to define further member functions of two_val<T>, like T get_second(), T add_first_and_second(), bool check_equality_of_first_and_second(), etc. For all these I would like to have corresponding lifted member functions for classes of the form trait_vector<two_val<T>>. However, what I would like to avoid is to copy and paste the definition of trait_vector<two_val<T>>::get_first() over and over again.

So what I'm aiming at is some member template function lift_from_two_val of class trait_vector that takes a member function of two_val<T> with return type U and turns it into a member function of trait_vector<two_val<T>> with return type trait_vector<U>. Then the lifts of the individual member functions of two_val<T> could be defined as one-liners by simply applying lift_from_two_val to the respective functions.

But I'm still too unfamiliar with the syntax of C++ to be sure if this is possible, let alone to actually make it work.

In case all of this is actually possible as I envision it: What if I want to lift from a bunch of other (template) classes to trait_vector? Would there again be a way around copy and pasting the code from lift_from_two_val?


Solution

  • template<std::invocable<T const> F>
    auto lift(F f) 
    {
        using RT = std::invoke_result_t<F, T>;
        trait_vector<RT> result{};
        result.reserve(vec.size());
        for(const auto& p : vec){
            result.vec.push_back(std::invoke(f, p));
        }
        return result;
    }
    

    now you can do this

    trait_vector<two_val<int>> bob;
    trait_vector<int> alice = bob.lift(&two_val<int>::get_first);
    trait_vector<int> eve = bob.lift(&two_val<int>::get_second);
    

    etc.

    Live example.