Recursive operator overloading for std::vector

This is my piece of code for overloading '+' for vector.

1
2
3
4
5
6
7
8
9
template<class T1, class T2>
auto operator+(const std::vector<T1>& vec1, const std::vector<T2>& vec2) -> std::vector<typename std::common_type<T1,T2>::type>
{
	size_t len = vec1.size();
	if(len != vec2.size()){throw std::invalid_argument ("Operand vectors have mismatched sizes."); }
	std::vector<typename std::common_type<T1,T2>::type> ret(len);
	for (size_t i = 0; i < len; i++) { ret[i] = vec1[i] + vec2[i]; }
	return ret;
}


And it only works if T1, T2 are integral types. To make it recursive, I changed it to,

1
2
3
4
5
6
7
8
9
template<class T1, class T2>
auto operator+(const std::vector<T1>& vec1, const std::vector<T2>& vec2) -> std::vector<decltype(vec1+vec2)>
{
	size_t len = vec1.size();
	if(len != vec2.size()){throw std::invalid_argument ("Operand vectors have mismatched sizes."); }
	std::vector<decltype(vec1+vec2)> ret(len);
	for (size_t i = 0; i < len; i++) { ret[i] = vec1[i] + vec2[i]; }
	return ret;
}


This piece also works for integral types and I thought that it would work for non-integral types too. But when I try to do this operation

1
2
3
4
5
int main(){
    vector<vector<double>> a = {{1,2,3},{4,5,6}};
    vector<vector<int>> b = {{4,5,6},{7,8,9}};
    cout << a+b << endl;
}


I get the errors,

 
recursively required by substitution of 'template<class T1, class T2> std::vector<decltype ((vec1 + vec2))> operator+(const std::vector<T>&, const std::vector<T2>&) [with T1 = std::vector<double>; T2 = std::vector<int>]'

 
required by substitution of 'template<class T1, class T2> std::vector<decltype ((vec1 + vec2))> operator+(const std::vector<T>&, const std::vector<T2>&) [with T1 = std::vector<double>; T2 = std::vector<int>]'

 
fatal error: template instantiation depth exceeds maximum of 900 (use -ftemplate-depth= to increase the maximum)


All on the operator function declaration line. Any idea what's going on and how can I make this work? Thank you.

The outstream is seperately overloaded like this

1
2
3
4
5
6
7
8
template<class T>
std::ostream& operator<<(std::ostream& os, const std::vector<T>& vec)
{
    os << '[';
    for (auto& n : vec) { os << n << ','; }
    os << ']';
    return os;
}

How do you know what decltype(vec1+vec2) is ... unless you have previously defined the operator + ?
Last edited on
Nearest that I could get was
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>
#include <vector>

template<class T>
std::ostream& operator << ( std::ostream& os, const std::vector<T>& vec)
{
    os << '[';
    for ( auto& n : vec ) { os << n << ','; }
    os << ']';
    return os;
}


template<class T1, class T2>
auto operator + ( const std::vector<T1>& vec1, const std::vector<T2>& vec2 ) -> std::vector< decltype( T1{}+T2{} ) >
{
	size_t len = vec1.size();
	// if( len != vec2.size() ){ throw std::invalid_argument ( "Operand vectors have mismatched sizes." ); }
	std::vector< decltype( T1{}+T2{} ) > ret(len);
	for ( size_t i = 0; i < len; i++ ) { ret[i] = vec1[i] + vec2[i]; }
	return ret;
}


int main(){
    std::vector<std::vector<double>> a = {{1,2,3},{4,5,6}};
    std::vector<std::vector<int>> b = {{4,5,6},{7,8,9}};
    std::cout << a + b << '\n';
}



But it's a bit unfriendly if you turn the double into a std::complex<double>. Which is sad.
Last edited on
> This is my piece of code for overloading '+' for vector.
> ...
> And it only works if T1, T2 are integral types.

It works for all types T1 and T2, provided
T1 + T2 is valid, and it yields a default constructible common type.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <vector>
#include <type_traits>
#include <stdexcept>
#include <string>

template<class T1, class T2>
auto operator+(const std::vector<T1>& vec1, const std::vector<T2>& vec2) -> std::vector<typename std::common_type<T1,T2>::type>
{
	size_t len = vec1.size();
	if(len != vec2.size()){throw std::invalid_argument ("Operand vectors have mismatched sizes."); }
	std::vector<typename std::common_type<T1,T2>::type> ret(len); // assumption: the common type is default-constructible
	for (size_t i = 0; i < len; i++) { ret[i] = vec1[i] + vec2[i]; } // assumption: the + operator yields a value that is convertible to the common type
	return ret;
}

int main()
{
    const std::vector< std::string > vec1 { "abcd", "efgh", "ijkl" } ;
    const std::vector< const char* > vec2 { "+mnop", "+qrst", "+uvwxyz" } ;

    for( const auto& v : vec1 + vec2 ) std::cout << v << ' ' ; // the common type is std::string
    std::cout << '\n' ;
}

http://coliru.stacked-crooked.com/a/268328ecf5c7264c
For the common vector type between two (compatible) vectors:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <iostream>
#include <vector>
#include <type_traits>
#include <stdexcept>
#include <string>

namespace detail
{
    template < typename T1, typename T2 > struct common_type : std::common_type<T1,T2> {} ;

    template < typename T1, typename T2 > // specialisation for the common vector type between two vectors
    struct common_type< std::vector<T1>, std::vector<T2> >
    {
        using type = std::vector< typename common_type<T1,T2>::type > ;
    };
}

template< typename T1, typename T2 >
auto operator+( const std::vector<T1>& vec1, const std::vector<T2>& vec2 )
{
        const std::size_t len = vec1.size();
	if(len != vec2.size()) throw std::invalid_argument ("Operand vectors have mismatched sizes.");
	std::vector< typename detail::common_type<T1,T2>::type > ret(len);
	for (size_t i = 0; i < len; i++) { ret[i] = vec1[i] + vec2[i]; }
	return ret;
}

int main()
{
    const std::vector< std::string > vec1 { "abcd", "efgh", "ijkl" } ;
    const std::vector< const char* > vec2 { "+mnop", "+qrst", "+uvwxyz" } ;

    for( const auto& v : vec1 + vec2 ) std::cout << v << ' ' ;
    std::cout << '\n' ;

    const std::vector<std::vector<double>> a = { {1.1,2.2,3.3}, {4.4,5.5,6.6} };
    const std::vector<std::vector<int>> b = { {4,5,6}, {7,8,9} };
    for( const auto& vec : a + b )
    {
        std::cout << "{ " ;
        for( const auto& v : vec ) std::cout << v << ' ' ;
        std::cout << "} " ;
    }
    std::cout << '\n' ;
}

http://coliru.stacked-crooked.com/a/1b3bb8a6f1c14b5d
@JLBorges, defining common type recursively is something I couldn't think of. Thanks for the help. This solves my question.
@lastchance, Thanks a lot. Now I see where I was tripping. The problem with complex is not your code. std::complex inherently rejects operations between different types.
Last edited on
Topic archived. No new replies allowed.