금번 C++ 공부에선, Function Overloading 과 Templates 부분을 공부하고 관련 내용을 실습해보겠다.
실습자료는 아래와 같다.
기본적으로 Function의 parameter에는 defulat 값을 넣을 수 있다.
int divide( int a, int b = 2 ) {
return a/b;
}
Function의 Overloading이란, Function들끼리 같은 이름을 Share하는데 Parameter type에 따라서 Return이 달라지는 것이다. 이 때, Return type만 가지고는 Function들 끼리 overload 할 수 없다.
다음은, Function Overload의 예시다.
보면, mySwap이라는 함수가 있는데, 이름이 다 같아도 input data의 type에 따라서 동작이 다 다르다.
이것이 가능한 이유는, Compiler가 함수들의 이름을 Managing 할 때, 각 function의 parameter type, 개수까지 고려해서 각 각을 Unique하게 관리하기 때문이다.
하지만, 위의 예시를 보면 같은 Functionality에 대해 data type별로 Function을 implement 해 놓았다.
이는 비효율적이며, 코드 구현/유지에 적잖은 에러를 초래한다.
그래서 등장한 개념이 Function Templates 다.
Template을 이용하면 어떤 Data type과도 function은 호응할 수 있다.
template <typename T>
void function(T &x, T&y ){
T temp = x;
x = y;
y = temp;
}
// SWAP 함수 using Template
Template이 사용된 Function을 Call할 때는, Function 에 < > 꺽쇠를 이용해서 Data type을 명시해줘야 한다.
int a = 5;
int b = 6;
mySwap<int>(a,b); // 이런식으로 < int > 명시
그런데, 위와 같은 경우는 명확히 a, b가 int이기 때문에 그냥 mySwap(a,b)를 써도 Compiler가 알아서 추론하여 동작을 시킬 수 있다.
다만, Compiler가 알아서 추론하기 힘든 Case의 경우 당연히 Template argument를 특정해줘야 한다.
그리고, Function은 복수의 Template을 받을 수도 있다.
Template을 Function의 Argument처럼 쓰는 경우도 있는데, 이를 Non-type template argument라고 한다.
template <int N>
int power(int base){
int ans = 1;
for(int i=0;i<N;i++){
ans *= base;
}
return ans;
}
int main(){
std::cout << " 2 ^ 3 = " << power<3>(2) << std::endl;
// 이런 식으로 Template의 N이 argument로 작용
}
이것의 장점은, Compiler가 Value를 compile time에 알 수 있어서 code 최적화 및 Error 탐색을 빨리 할 수 있다.
이것의 단점은, Template Value(위에서 N)을 사용할 때마다 특정해줘야 하고, 이 value로 인해 compiled binary크기가 커진다.
지금까지 배운 내용을 정리하면 아래와 같다.
이제 이 부분을 실습해보자!
본 실습을 통해서 Template 개념을 이해할 수 있다.
# exercise.h 파일
#ifndef EXERCISE_H
#define EXERCISE_H
#include <vector>
#include <stdexcept>
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
/*=============================================== TODO 1 ===============================================
1. Instruction:
- Implement a function `addVectors`.
2. Function Definition:
- A template function that performs element-wise addition of two vectors of type T.
- The function should be able to take vectors of any data type (e.g., int, float, double)
3. Parameters:
- First parameter: A constant reference to v1 of type vector<T>. This input vector remains unmodified.
- Second parameter: A constant reference to v2 of type vector<T>. This input vector remains unmodified.
4. Return:
- It returns a new vector of type vector<T> containing the element-wise sum of `v1` and `v2`.
Each element of the resulting vector is the sum of the corresponding elements from the input vectors.
5. Preconditions:
- Both input vectors `v1` and `v2` must be of the same size.
- Hint: Use `throw invalid_argument("Vectors must have the same size.");`
=======================================================================================================*/
// Your code here:
template<typename T>
vector<T> addVectors(const vector<T> &v1,const vector<T> &v2){
// Throw 적용
if(v1.size()!=v2.size()){
throw invalid_argument("Vectors must have the same size~~");
}
int n = v1.size();
vector<T> temp;
for(int i=0;i<n;i++){
temp.push_back(v1[i]+v2[i]);
}
return temp;
}
/*=============================================== TODO 2 ===============================================
1. Instruction:
- Implement a function `dotProduct'.
2. Function Definition:
- A template function that computes the dot product of two vectors of type T.
- The function should take vectors of any data type (e.g., int, float, double)
3. Parameters:
- First parameter: A constant reference to `v1` of type vector<T>. This is the first input vector, and it remains unmodified.
- Second parameter: A constant reference to `v2` of type vector<T>. This is the second input vector, and it remains unmodified.
4. Return:
- It returns a value of type T, representing the dot product of the two input vectors.
The dot product is computed by summing the products of corresponding elements from `v1` and `v2`.
5. Preconditions:
- Both input vectors `v1` and `v2` must be of the same size.
=======================================================================================================*/
// Your code here:
template<typename A>
A dotProduct(vector<A> &v1,vector<A> &v2){
if(v1.size()!=v2.size()){
throw invalid_argument("Vectors must have the same size!!");
}
int n = v1.size();
A temp = 0;
for(int i=0;i<n;i++){
temp += (v1[i]*v2[i]);
}
return temp;
}
/*==================================== Do not modify this section =====================================*/
template<typename T>
vector<T> inputVector(const string& vectorName) {
int size;
vector<T> vec;
cout << "Please enter the size of " << vectorName << ": ";
cin >> size;
if (size <= 0) {
throw invalid_argument("The size of vector must be greater than 0");
}
cout << "Please enter the elements of " << vectorName << " (separate the elements with spaces and press Enter when you are done)." << endl;
cin.ignore(numeric_limits<streamsize>::max(), '\n');
string line;
getline(cin, line);
istringstream stream(line);
for (int i = 0; i < size; ++i) {
string valueStr;
if (!(stream >> valueStr)) {
throw invalid_argument("Invalid input detected! Expected type does not match.");
}
if constexpr (is_same<T, int>::value) {
if (valueStr.find('.') != string::npos) {
throw invalid_argument("Invalid input detected! Expected an integer, but received a floating-point number.");
}
char* end;
long value = strtol(valueStr.c_str(), &end, 10);
if (*end != '\0' || value > numeric_limits<int>::max() || value < numeric_limits<int>::min()) {
throw invalid_argument("Invalid input detected! Expected an integer.");
}
vec.push_back(static_cast<int>(value));
} else {
istringstream valueStream(valueStr);
T value;
if (!(valueStream >> value) || !valueStream.eof()) {
throw invalid_argument("Invalid input detected! Expected type does not match.");
}
vec.push_back(value);
}
}
T extra;
if (stream >> extra) {
throw invalid_argument("Too many elements entered for " + vectorName + "! Expected " + to_string(size) + " values.");
}
return vec;
}
/*=====================================================================================================*/
template<typename T>
void processVectors() {
vector<T> v1 = inputVector<T>("vector1");
vector<T> v2 = inputVector<T>("vector2");
/*=============================================== TODO 3 ===============================================
1. Instruction:
- Use the `addVectors` function to compute the element-wise sum of `v1` and `v2`.
- Assign the result to a variable of type `vector<T>` named `result`.
- Ensure that the return type of `addVectors` matches the type `vector<T>`.
======================================================================================================*/
// your code here:
vector<T> result = addVectors(v1,v2);
/*==================================== Do not modify this section =====================================*/
cout << "\nResult of `addVectors`: ";
for (int i = 0; i < v1.size(); ++i) {
cout << result[i] << " ";
}
cout << endl;
/*============================================= TODO 4 =============================================
1. Instruction:
- Use the `dotProduct` function to compute the dot product of `v1` and `v2`.
- Display the result of the `dotProduct` function using `cout` in the following format: "Result of `dotProduct`: ".
===================================================================================================*/
// your code here:
// T result2 = dotProduct(v1,v2);
cout << "Result of 'dotProduct': "<< dotProduct(v1,v2) <<endl;
}
#endif
# test case 파일
#include <iostream>
#include <vector>
#include <cmath>
#include "9_exercise.h"
using namespace std;
template<typename T>
bool compareVectors(const vector<T>& v1, const vector<T>& v2, T tolerance = static_cast<T>(1e-9)) {
if (v1.size() != v2.size()) return false;
for (size_t i = 0; i < v1.size(); ++i) {
if constexpr (is_floating_point<T>::value) {
if (abs(v1[i] - v2[i]) > tolerance) return false;
} else {
if (v1[i] != v2[i]) return false;
}
}
return true;
}
bool runTests() {
bool all_tests_passed = true;
// Test case 1: Vectors of type int
vector<int> v1_int = {1, 2, 3};
vector<int> v2_int = {4, 5, 6};
vector<int> expected_sum_int = {5, 7, 9};
int expected_dot_product_int = 32;
cout << "Test Case 1: int vectors\n";
vector<int> result_int = addVectors(v1_int, v2_int);
int dot_product_int = dotProduct(v1_int, v2_int);
if (!compareVectors(result_int, expected_sum_int)) {
cout << "Test Case 1 Failed: Incorrect vector sum.\n";
all_tests_passed = false;
} else if (dot_product_int != expected_dot_product_int) {
cout << "Test Case 1 Failed: Incorrect dot product.\n";
all_tests_passed = false;
} else {
cout << "Test Case 1 Passed.\n";
}
// Test case 2: Vectors of type float
vector<float> v1_float = {1.1f, 2.2f, 3.3f};
vector<float> v2_float = {4.4f, 5.5f, 6.6f};
vector<float> expected_sum_float = {5.5f, 7.7f, 9.9f};
float expected_dot_product_float = 38.72f;
cout << "\nTest Case 2: float vectors\n";
vector<float> result_float = addVectors(v1_float, v2_float);
float dot_product_float = dotProduct(v1_float, v2_float);
if (!compareVectors(result_float, expected_sum_float)) {
cout << "Test Case 2 Failed: Incorrect vector sum.\n";
all_tests_passed = false;
} else if (abs(dot_product_float - expected_dot_product_float) > 1e-5) {
cout << "Test Case 2 Failed: Incorrect dot product.\n";
all_tests_passed = false;
} else {
cout << "Test Case 2 Passed.\n";
}
// Test case 3: Vectors of type double
vector<double> v1_double = {1.1, 2.2, 3.3};
vector<double> v2_double = {4.4, 5.5, 6.6};
vector<double> expected_sum_double = {5.5, 7.7, 9.9};
double expected_dot_product_double = 38.72;
cout << "\nTest Case 3: double vectors\n";
vector<double> result_double = addVectors(v1_double, v2_double);
double dot_product_double = dotProduct(v1_double, v2_double);
if (!compareVectors(result_double, expected_sum_double)) {
cout << "Test Case 3 Failed: Incorrect vector sum.\n";
all_tests_passed = false;
} else if (abs(dot_product_double - expected_dot_product_double) > 1e-9) {
cout << "Test Case 3 Failed: Incorrect dot product.\n";
all_tests_passed = false;
} else {
cout << "Test Case 3 Passed.\n";
}
// Final test result summary
if (all_tests_passed) {
cout << "\nAll test cases passed.\n";
} else {
cout << "\nSome test cases failed.\n";
}
return all_tests_passed;
}
int main() {
if (runTests()) {
try {
int type;
cout << "\nDo the test yourself"<<endl;
cout << "Choose type of the vectors (1: int, 2: float, 3: double): ";
cin >> type;
switch (type) {
case 1:
/*============================================= TODO 5 =============================================
1. Instruction:
- Call the `processVectors` function with `int` as the template argument.
====================================================================================================*/
// Your code here:
processVectors<int>();
break;
case 2:
/*============================================= TODO 6 =============================================
1. Instruction:
- Call the `processVectors` function with `float` as the template argument.
====================================================================================================*/
// Your code here:
processVectors<float>();
break;
case 3:
/*============================================= TODO 7 =============================================
1. Instruction:
- Call the `processVectors` function with `double` as the template argument.
====================================================================================================*/
// Your code here:
processVectors<double>();
break;
default:
cout << "잘못된 타입을 입력하셨습니다." << endl;
return 0;
}
} catch (const std::invalid_argument& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
} else {
return 0;
}
}
'SW 만학도 > C++ & Algorithm' 카테고리의 다른 글
VS code C++ Debugging & Operator overloading (0) | 2025.02.03 |
---|---|
C++ Object Oriented Programming(OOP), Overroad, Override 실습 (0) | 2025.01.09 |
Heap Implementation [1] (0) | 2024.12.26 |
Review 11 - Dynamic Programming (0) | 2024.08.03 |
Review 10 - Single-Source-Shortest Path in C++ (0) | 2024.08.02 |