Search code examples
c++templatesvariadic-templates

variadic template for sorting variable number of arrays


I am trying to write a program to sort unsorted 'n' vectors using variadic templates into one sorted vector. It can be done in other ways but I want to do it this way to better my understanding of variadic templates. I don't know if I can achieve that but below is my attempt at it which is not working.

My understanding is that ultimately sorted_n_vector will be reduced to MergeVector<int> + MergeVector<int> + MergeVectorMergeVector<int> same as line 50(MergeVector a8(a5 + a6 + a7)) but I think it is wrong. Please help and also give me some code review comments as well.

#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
#include <functional>
#include <string>

template<typename t>
class MergeVector {
        public:
        std::vector<t> arr_;

        MergeVector(std::vector<t> arr) : arr_(arr) {}

        auto operator + (MergeVector<t> &obj) {
                std::vector<t> dst;
                std::sort(arr_.begin(), arr_.end());
                std::sort(obj.arr_.begin(), obj.arr_.end());
                std::merge(arr_.begin(), arr_.end(), obj.arr_.begin(), obj.arr_.end(), std::back_inserter(dst));
                MergeVector res(dst);
                return res;
        }

        friend auto& operator<<(std::ostream &os, MergeVector<t>& mv)
        {
                std::copy(mv.arr_.begin(), mv.arr_.end(), std::ostream_iterator<t>(os, " "));
                return os;
        }
};

template<typename t>
auto sorted_n_vector(t vec)
{
        return vec;
}

template<typename t, typename... vectors>
auto sorted_n_vector(t vec, vectors... args)
{
        return vec + sorted_n_vector(args...);
}

int main() {
        std::vector<int> a1 = {1, 2, 3};
        std::vector<int> a2 = {4, 5, 6};
        std::vector<int> a3 = {7, 8, 9};
        MergeVector<int> a5(a1);
        MergeVector<int> a6(a2);
        MergeVector<int> a7(a3);
        MergeVector<int> a8(a5 + a6 + a7); //---- Line 50
        std::cout << a8 << std::endl;

#if 1
        MergeVector<int> a9(sorted_n_vector(a5, a6, a7));
        std::cout << a9 << std::endl;
#endif

        return 0;
}

I am getting below error:

test.cpp:40:20: error: invalid operands to binary expression ('MergeVector<int>' and 'MergeVector<int>')
        return vec + sorted_n_vector(args...);
               ~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~
test.cpp:40:22: note: in instantiation of function template specialization 'sorted_n_vector<MergeVector<int>, MergeVector<int> >' requested here
        return vec + sorted_n_vector(args...);
                     ^
test.cpp:54:22: note: in instantiation of function template specialization 'sorted_n_vector<MergeVector<int>, MergeVector<int>, MergeVector<int> >' requested here
        MergeVector<int> a9(sorted_n_vector(a5, a6, a7));
                            ^
test.cpp:15:7: note: candidate function not viable: expects an l-value for 1st argument
        auto operator + (MergeVector<t> &obj) { 

Solution

  • sorted_n_vector has a return type specified as auto, therefore it will return a temporary object, not a reference to some other object.

    The you try to call the operator+ with this temporary as argument, but operator+ expects a non-const (lvalue) reference parameter.

    Binding temporaries (or rather rvalue expressions) to non-const (lvalue) references is forbidden, therefore the compiler will not consider calling your operator+ and will instead search somewhere else.

    Not finding any alternative call for +, it gives you the error message you are seeing.

    The solution is to take the parameter of operator+ as const reference:

    auto operator + (const MergeVector<t> &obj) {
    

    Binding temporaries (rvalue expressions) to const (lvalue) references is allowed and works as one would expect.

    Edit:

    However this will raise another error, because your operator+ is actually modifying obj and this. While you can solve this by taking the argument by-value rather than by-reference, that is an unusual design. Typically + is expected to not modify its arguments. It would be surprising if c = a+b fails because a or b are not allowed to be modified. You can achieve this by doing the sorting in your MergeVector constructor:

    MergeVector(std::vector<t> arr) : arr_(arr) {
        std::sort(arr_.begin(), arr_.end());
    }
    

    Then you can drop the std::sort lines in operator+ and you can make it completely const in both arguments:

    auto operator + (const MergeVector<t> &obj) const {