c++templatestemplate-specializationpartial-specialization

Partial specialization of a class template member function


I have defined two class templates, class two_val and class trait_vector as follows:

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

        two_val() :
            first_val {},
            second_val {}
        {}
        
        T get_first() const {return first_val;}
};

and:

template<class T>
class trait_vector{
    private:
    
        std::vector<T> vec;
                
    public:
        trait_vector() :
            vec {}
        {}
};

Now, for the case that I have a trait_vector of two_val<T>s of any class T, I would like to provide a function that returns me the trait_vector<T> of the respective first_vals.

I managed to implement this by specializing the class trait_vector as follows:

template<class T>
class trait_vector<two_val<T>>{
    private: 
        std::vector<two_val<T>> vec;
                
    public:
        trait_vector() :
            vec {}
        {}
        
        trait_vector<T> get_first() {
            trait_vector<double> result {};
            for(unsigned i=0; i < vec.size(); ++i){
                result.vec.push_back(vec[i].get_first());
            }
            return result;

        }
};

But I would like to avoid copy and pasting the entire rest of the class. It feels like there should be a shorter solution (especially since the classes I'm actually working with are much bigger).

However, every direct solution that I tried ran into the problem that function templates cannot be partially specialized. This answer explains how to use a helper class for the desired function in a related case. However, I did not manage the transfer to my situation.


Solution

  • First create a trait is_two_val:

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

    Having typedef in your two_val class might help (else might be done with externl traits)

    template<class T>
    class two_val{
    public:
        using type = T;
    // ...
    };
    

    then you might SFINAE or requires (C++20) for your member with that traits:

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