1

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.

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
  • 2
    Does it actually need to be a member function? Could it just be a global function instead? – Alan Birtles Apr 28 '23 at 13:16
  • The objective is to provide an additional function that will be available only for a particular specialization of trait_vector. The title of the question could be changed to indicate this more clearly. – Hari Apr 28 '23 at 20:18

1 Answers1

3

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;
    }
};
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • Thank you, I made my code work with your suggestion and everything seems fine! In the definition of the trait `is_two_val` it has to be `template` instead of `template`, right? – Manuel Du Apr 28 '23 at 15:15
  • Indeed, typo fixed. – Jarod42 Apr 28 '23 at 15:27
  • Now I still ran into a problem. As long as I actually only defined variables of type `trait_vector>` for some `T` everything worked indeed fine. But now I tried to declare a variable of type `trait_vector` and the compiler (g++ 12.2.0) complained "error: ‘double’ is not a class, struct, or union type". Something still seems to be wrong around the `requires` syntax. – Manuel Du May 01 '23 at 10:20
  • Need to change return type to `auto`. `T::type` is indeed invalid for `T=double` in a place where T is fixed (it is a template of the class, not of the member function). – Jarod42 May 01 '23 at 11:36
  • Thanks again, I think now it all works out. May I interest you in my [follow-up question](https://stackoverflow.com/q/76147846/21611679)? – Manuel Du May 01 '23 at 14:51