1

This program adds nested vectors, but does not promote the types correctly. I think std::plus needs to take in T1 or T2 depending on basic type promotion rules. The original problem is in this post (decltype does not resolve nested vectors. How can I use templates for nested vectors?).

main.cpp

#include <algorithm>
#include <iostream>
#include <vector>

template<typename T1>
std::ostream& operator<<(std::ostream& stream, std::vector<T1> r){
    if(r.size() == 0){
        return stream;
    }
    else{
        stream << "(";
        for(int i = 0; i < r.size(); i++){
            if(i < (r.size() - 1)){
                stream << r[i] << ", ";
            }
            else{
                stream << r[i] << ")";
            }
        }
    }
    return stream;
};

template <typename T1, typename T2>
struct Add : std::plus<T1> { };//<-Here T1 or T2 depending on their types.

template <typename T1, typename T2>
struct Add<std::vector<T1>, std::vector<T2>>
{   
    auto operator()(const std::vector<T1>& l, const std::vector<T2>& r)
        -> std::vector<decltype(Add<T1,T2>{}(l[0], r[0]))>
    {
        using type = decltype(Add<T1,T2>{}(l[0], r[0]));
        std::vector<type> ans;

        if(l.size() == std::max(l.size(),r.size()))
            std::transform(r.begin(), r.end(), l.begin(), std::back_inserter(ans), Add<T1,T2>{});
        else
            std::transform(l.begin(), l.end(), r.begin(), std::back_inserter(ans), Add<T1,T2>{});
        return ans;
    };
};

template <typename T1, typename T2>
auto operator+(const std::vector<T1>& lhs, const std::vector<T2>& rhs)
    -> decltype(Add<std::vector<T1>, std::vector<T2>>{}(lhs, rhs))
{
    return Add<std::vector<T1>, std::vector<T2>>{}(lhs, rhs);
}

int main(){
    std::vector<int> e = {1};
    std::vector<double> q = {1.5};

    //Incorrect result = 2
    std::cout << (e + q) << std::endl;
    //Correct result = 2.5
    std::cout << (q + e) << std::endl;

    return 0;
}
Community
  • 1
  • 1
dylan
  • 289
  • 2
  • 11

2 Answers2

1

Use std::common_type when defining the primary template Add<>, like this (must #include <type_traits>):

template <typename T1, typename T2>
struct Add : std::plus<typename std::common_type<T1,T2>::type> { };//<-Here T1 or T2 depending on their types.

You basically tell the Add primary template to use the "best" type. Then the specialization will use the primary, and the latter now takes care of the promotion.

Live on Coliru

vsoftco
  • 55,410
  • 12
  • 139
  • 252
  • 1
    @dylan Because `type` it is a dependent type (depends on the template parameters `T1` and `T2`), [see this question](http://stackoverflow.com/questions/610245/where-and-why-do-i-have-to-put-the-template-and-typename-keywords) for more details. – vsoftco Jun 16 '15 at 22:46
  • Unfortunately, now when I try adding nested vectors of different types I get an compiler error. – dylan Jun 16 '15 at 23:00
  • So, something like this will not work: ` `std::vector> e = {{1},{1}}; std::vector> q = {{1.5},{1.5}}; std::cout << (e + q) << std::endl;` – dylan Jun 16 '15 at 23:04
  • But I was able to add them before when they were only one dimensional vectors. Now I cant add different type nested vectors. – dylan Jun 16 '15 at 23:13
  • This works just fine: `std::vector e = {1}; std::vector q = {1.5}; std::cout << (e + q) << std::endl;` – dylan Jun 16 '15 at 23:14
1

There are two issues in your code (that I got wrong in my answer to your original question). The first, as mentioned by vsoftco is using std::common_type for the base case.

The second is a simple error. Consider this if statement:

    if(l.size() == std::max(l.size(),r.size()))
        std::transform(r.begin(), r.end(), l.begin(), std::back_inserter(ans), 
                       Add<T1,T2>{});
    else
        std::transform(l.begin(), l.end(), r.begin(), std::back_inserter(ans), 
                       Add<T1,T2>{});

Add<T1,T2>::operator(), in the partial specialization case, takes two arguments: a std::vector<T1> and a std::vector<T2>, in that order. But in the two branches of the if statement, we're actually calling them in different orders. In the true branch, we're calling them in the REVERSE order, so just have to invert the ordering in the template:

    if(l.size() == std::max(l.size(),r.size()))
        std::transform(r.begin(), r.end(), l.begin(), std::back_inserter(ans), 
                       Add<T2,T1>{});

Should actually just remove the if-statement altogether so that the specialization case is now:

template <typename T1, typename T2> 
struct Add<std::vector<T1>, std::vector<T2>>
{   
    using R = decltype(Add<T1,T2>{}(std::declval<T1>(), std::declval<T2>()));

    std::vector<R> operator()(const std::vector<T1>& l, const std::vector<T2>& r)
    {
        std::vector<R> ans;

        std::transform(l.begin(),
                       l.begin() + std::min(l.size(), r.size()),
                       r.begin(),
                       std::back_inserter(ans),
                       Add<T1,T2>{});
        return ans;
    };
};
Community
  • 1
  • 1
Barry
  • 286,269
  • 29
  • 621
  • 977