I need to write a generic function that finds sum of elements that belong to two containers and put those elements inside a vector which type is the result of sum.
EXAMPLE:
container one: 5 2 8 3 6
container two: 6 1 5
container of sum: 11 3 13 3 6
#include <iostream>
#include <cmath>
#include <vector>
template < typename tip1, typename tip2, typename tip >
tip sum_of_containers(tip1 blok1, tip2 blok2) {
std::vector < tip > a;
int n1 = std::distance(blok1.begin(), blok1.end());
int n2 = std::distance(blok2.begin(), blok2.end());
int n;
n = n1;
if (n2 > n1) n = n2;
for (int i = 0; i < n; i++)
a[i].push_back(blok1[i] + blok2[i]);
return a;
}
int main() {
int n1, n2, x;
std::cin >> n1;
std::vector < double > a, b, c;
for (int i = 0; i < n1; i++) {
std::cin >> x;
a.push_back(x);
}
std::cin >> n2;
for (int i = 0; i < n2; i++) {
std::cin >> x;
b.push_back(x);
}
c = sum_of_containers(a, b);
for (double i: c)
std::cout << i << " ";
return 0;
}
errors on line 31:
no matching function for call to 'sum_of_containers
couldn't deduce template parameter 'tip'
Could you give me some approach or idea how to solve this problem?
The question is very unclear.
"Generic" could mean different containers, like std::vector
or std::deque
. But in your function function you are using a std::vector
and the index operator[]
and the function push_back
. The only 2 containers having all this are std::vector
and std::deque
. So, this would not make that much sense.
The next level of "generic" would be to have different data types for a std::vector
. But with 3 template parameters, this would mean worst case that we add 2 different data types and assign them to a 3rd, again different data type.
Logically this would create a lot of other troubles, type casting would be needed and loss of precision could be the result.
If we look in the function main, then we see, that 3 std::vectors
, all with the same data type double
are instantiated. This makes sense. And this would restrict the "generic" function to have one common templatized parameter for a type that a std::vector
would hold.
This could then look like the following:
#include <iostream>
#include <vector>
#include <algorithm>
template <typename T>
std::vector<T> sumOfVectors(const std::vector<T>& t1, const std::vector<T>& t2) {
// Get a reference to the larger vector
const std::vector<T>& largerVector = (t1.size() > t2.size()) ? t1 : t2;
// Create the resulting vector that can hold all elements
std::vector<T> result(largerVector.size(), {});
size_t index{};
for (; index < std::min(t1.size(),t2.size()); ++index)
result[index] = t1[index] + t2[index];
for (; index < largerVector.size(); ++index)
result[index] = largerVector[index];
return result;
}
int main() {
int n1, n2, x;
std::cin >> n1;
std::vector < double > a, b, c;
for (int i = 0; i < n1; i++) {
std::cin >> x;
a.push_back(x);
}
std::cin >> n2;
for (int i = 0; i < n2; i++) {
std::cin >> x;
b.push_back(x);
}
c = sumOfVectors(a, b);
for (double i : c)
std::cout << i << " ";
return 0;
}
But of course you would define an operator +
for this, which gives us a more intuitive result:
#include <iostream>
#include <vector>
#include <algorithm>
template <typename T>
std::vector<T> operator +(const std::vector<T>& t1, const std::vector<T>& t2) {
// Get a reference to the larger vector
const std::vector<T>& largerVector = (t1.size() > t2.size()) ? t1 : t2;
// Create the resulting vector that can hold all elements
std::vector<T> result(largerVector.size(), {});
size_t index{};
for (; index < std::min(t1.size(),t2.size()); ++index)
result[index] = t1[index] + t2[index];
for (; index < largerVector.size(); ++index)
result[index] = largerVector[index];
return result;
}
int main() {
int n1, n2, x;
std::cin >> n1;
std::vector < double > a, b, c;
for (int i = 0; i < n1; i++) {
std::cin >> x;
a.push_back(x);
}
std::cin >> n2;
for (int i = 0; i < n2; i++) {
std::cin >> x;
b.push_back(x);
}
c = a + b;
for (double i : c)
std::cout << i << " ";
return 0;
}
With the exact task description given in the comment, we can come up with the needed solution.
It is a little bit more heavy.
We could even add type traits to check, if the containers are iterable, but maybe that is too much (though easy in C++20 with something like if constexpr (std::ranges::range<Container>)
Anyway, please see the updated solution with 2 test cases.
#include <iostream>
#include <type_traits>
#include <vector>
#include <deque>
#include <forward_list>
#include <array>
#include <list>
#include <string>
// Some aliases to avoid heavy typing
template <typename T, typename U>
using Value_t = typename std::common_type<typename T::value_type, typename U::value_type>::type;
template <typename T, typename U>
using Vector_t = typename std::vector<Value_t<T, U>>;
template <typename T, typename U>
auto sum_of_containers(const T& c1, const U& c2) -> Vector_t<T, U> {
// Get rid of template parameters using aliases
using MyType = Value_t<T,U>;
using MyVector = Vector_t<T, U>;
// Here we will store the result
MyVector result{};
typename T::const_iterator c1Iter = std::begin(c1);
typename U::const_iterator c2Iter = std::begin(c2);
// Add, as long as there are the same number of elements in the containers
while ((c1Iter != std::end(c1)) and (c2Iter != std::end(c2)))
result.push_back(static_cast<MyType>(*c1Iter++) + static_cast<MyType>(*c2Iter++));
// If there should still be elements in one of the containers, then add them to the resulting vector as is
while (c1Iter != std::end(c1))
result.push_back(static_cast<MyType>(*c1Iter++));
while (c2Iter != std::end(c2))
result.push_back(static_cast<MyType>(*c2Iter++));
return result;
}
int main() {
// Test Data 0
std::deque<float> fl1 { 0.1f, 0.2f, 0.3f };
std::deque<double> dbl1 { 0.1, 0.2, 0.3, 0.4, 0.5 };
auto result0 = sum_of_containers(fl1, dbl1);
for (const auto& x0 : result0)
std::cout << x0 << '\n';
std::cout << '\n';
// Test Data 1
std::deque<int> dq{ -1,2,-3 };
std::forward_list<float> fl{ 0.1f, 0.2f, 0.3f, 0.4f, 0.5f };
auto result1 = sum_of_containers(dq, fl);
for (const auto& x1 : result1)
std::cout << x1 << '\n';
std::cout << '\n';
// Test Data 2
std::array<unsigned long, 3> ar1{ 1ul,2ul,3ul };
std::list<double> dbl{ 0.1, 0.2, 0.3, 0.4, 0.5 };
auto result2 = sum_of_containers(ar1, dbl);
for (const auto& x2 : result2)
std::cout << x2 << '\n';
std::cout << '\n';
}
.
.
.
And of course, but not needed, you would implement also the + operator here.
#include <iostream>
#include <type_traits>
#include <vector>
#include <deque>
#include <forward_list>
#include <array>
#include <list>
#include <string>
// Some aliases to avoid heavy typing
template <typename T, typename U>
using Value_t = typename std::common_type<typename T::value_type, typename U::value_type>::type;
template <typename T, typename U>
using Vector_t = typename std::vector<Value_t<T, U>>;
template <typename T, typename U>
auto operator +(const T& c1, const U& c2) -> Vector_t<T, U> {
// Get rid of template parameters using aliases
using MyType = Value_t<T, U>;
using MyVector = Vector_t<T, U>;
// Here we will store the result
MyVector result{};
typename T::const_iterator c1Iter = std::begin(c1);
typename U::const_iterator c2Iter = std::begin(c2);
// Add, as long as there are the same number of elements in the containers
while ((c1Iter != std::end(c1)) and (c2Iter != std::end(c2)))
result.push_back(static_cast<MyType>(*c1Iter++) + static_cast<MyType>(*c2Iter++));
// If there should still be elements in one of the containers, then add them to the resulting vector as is
while (c1Iter != std::end(c1))
result.push_back(static_cast<MyType>(*c1Iter++));
while (c2Iter != std::end(c2))
result.push_back(static_cast<MyType>(*c2Iter++));
return result;
}
int main() {
// Test Data 0
std::deque<float> fl1{ 0.1f, 0.2f, 0.3f };
std::deque<double> dbl1{ 0.1, 0.2, 0.3, 0.4, 0.5 };
auto result0 = fl1 + dbl1;
for (const auto& x0 : result0)
std::cout << x0 << '\n';
std::cout << '\n';
// Test Data 1
std::vector<int> ve{ -1,2,-3 };
std::forward_list<float> fl{ 0.1f, 0.2f, 0.3f, 0.4f, 0.5f };
auto result1 = ve + fl;
for (const auto& x1 : result1)
std::cout << x1 << '\n';
std::cout << '\n';
// Test Data 2
std::array<unsigned long, 3> ar1{ 1ul,2ul,3ul };
std::list<double> dbl2{ 0.1, 0.2, 0.3, 0.4, 0.5 };
auto result2 = ar1 + dbl2;
for (const auto& x2 : result2)
std::cout << x2 << '\n';
std::cout << '\n';
}