모던 C++ 유틸리티 함수 활용
개요
utils.h
헤더 파일에 정의된 다양한 유틸리티 함수를 활용하여 C++11, C++17, C++20 등 모던 C++의 주요 표준 기능들을 사용하는 방법을 알아봅니다. 템플릿, constexpr
, std::string_view
, 구조적 바인딩(structured bindings) 등 코드의 재사용성과 가독성, 성능을 높여주는 기능들을 실제 예제를 통해 학습합니다.
C++ 버전별 주요 키워드 도입 시기
- C++11
constexpr
: 컴파일 타임에 값을 계산할 수 있는 상수 표현식입니다.std::array
: 정적 크기의 배열을 위한 컨테이너입니다.std::vector
,std::map
: 개선된 표준 라이브러리 컨테이너입니다.- 범위 기반 for 루프 (Range-based for loop): 컨테이너의 모든 요소를 쉽게 순회할 수 있습니다.
- 템플릿 기능 향상: 가변 인자 템플릿 등
- C++17
std::string_view
: 문자열을 소유하지 않고 "뷰"만 제공하여 불필요한 복사를 방지하고 성능을 향상시킵니다.- 구조적 바인딩 (Structured bindings):
std::pair
,std::tuple
, 구조체 등의 멤버를 간결하게 개별 변수로 분해할 수 있습니다. if constexpr
: 컴파일 타임에if
문의 분기를 결정할 수 있습니다.
내용 설명
utils.h
와 main.cpp
예제는 다음과 같은 모던 C++의 핵심 기능들을 보여줍니다.
- 템플릿 함수 (Template Functions)
print_array
,print_vector
함수는 템플릿을 사용하여 다양한 데이터 타입(e.g.,int
,double
)의 배열과 벡터를 출력할 수 있습니다. 이는 코드 중복을 줄이고 일반적인(generic) 코드를 작성하게 해줍니다.print_vector
는std::vector<std::pair<std::string, std::size_t>>
타입에 대해 템플릿 특수화(template specialization) 되어 있어, 해당 타입의 벡터를 특별한 형식으로 출력합니다.
constexpr
상수pi
값을constexpr
로 선언하여 컴파일 타임 상수로 만들었습니다. 이를 통해 런타임 비용 없이 값을 사용할 수 있으며, 컴파일러가 더 많은 최적화를 수행할 수 있습니다.
- 구조적 바인딩 (Structured Bindings)
print_map
함수 내의for (const auto &[Key, value] : Map)
구문은 C++17의 구조적 바인딩을 사용합니다.std::map
의 각 원소(std::pair
)를Key
와value
라는 두 개의 변수로 즉시 분해하여 코드 가독성을 크게 향상시킵니다.
std::string_view
readFile
함수는 파일 경로를std::string_view
로 받습니다.std::string
객체를 생성하고 복사하는 대신, 기존 문자열 데이터를 가리키는 뷰를 사용하므로 오버헤드가 적습니다. 파일 경로처럼 읽기만 하는 문자열을 전달할 때 매우 효율적입니다.
예제 코드
src/utils.h
#ifndef UTILS_H
#define UTILS_H
#include <array>
#include <cmath>
#include <fstream>
#include <iostream>
#include <map>
#include <random>
#include <sstream>
#include <string_view>
#include <unordered_map>
#include <vector>
constexpr double pi = 3.14159265358979311600;
template <typename T>
void print_array(const T *array, const std::size_t length)
{
for (std::size_t i = 0; i < length - 1; i++)
{
std::cout << array[i] << ", ";
}
std::cout << array[length - 1] << '\n';
}
template <typename T, std::size_t S>
void print_array(const std::array<T, S> array)
{
for (std::size_t i = 0; i < array.size() - 1; i++)
{
std::cout << array[i] << ", ";
}
std::cout << array[array.size() - 1] << '\n';
}
template <typename T>
void print_vector(const std::vector<T> &vector)
{
for (std::size_t i = 0; i < vector.size() - 1; i++)
{
std::cout << vector[i] << ", ";
}
std::cout << vector[vector.size() - 1] << '\n';
}
template <>
void print_vector(
const std::vector<std::pair<std::string, std::size_t>> &vector)
{
for (std::size_t i = 0; i < vector.size() - 1; i++)
{
std::cout << vector[i].first << ": " << vector[i].second << ", ";
}
std::cout << vector[vector.size() - 1].first << ": "
<< vector[vector.size() - 1].second << '\n';
}
template <typename T, typename U>
void print_map(const std::map<T, U> Map)
{
for (const auto &[Key, value] : Map)
{
std::cout << Key << ": " << value << '\n';
}
}
std::string readFile(std::string_view file_path)
{
auto str = std::string{};
auto text = std::string{};
auto iffile = std::ifstream{};
iffile.open(file_path.data());
if (iffile.is_open())
{
while (std::getline(iffile, str))
{
text += str + '\n';
}
}
iffile.close();
return text;
}
template <typename T>
void random_vector(std::vector<T> &vec)
{
std::mt19937 random_generator(22);
std::uniform_int_distribution<T> random_distribution(-10, 10);
for (auto &val : vec)
{
val = random_distribution(random_generator);
}
}
void clear_console()
{
#if defined _WIN32
system("cls");
#elif defined(__LINUX__) || defined(__gnu_linux__) || defined(__linux__)
system("clear");
#elif defined(__APPLE__)
system("clear");
#else
system("clear");
#endif
}
#endif /* UTILS_H */
src/main.cpp
#include "utils.h"
int main()
{
// 1. C-style array and std::array with templates
std::cout << "1. C-style array and std::array with templates" << std::endl;
int c_style_arr[] = {1, 2, 3, 4, 5};
std::array<double, 5> std_arr = {1.1, 2.2, 3.3, 4.4, 5.5};
print_array(c_style_arr, 5);
print_array(std_arr);
std::cout << std::endl;
// 2. std::vector with template specialization
std::cout << "2. std::vector with template specialization" << std::endl;
std::vector<int> int_vec = {10, 20, 30};
std::vector<std::pair<std::string, std::size_t>> pair_vec = {{"apple", 5}, {"banana", 3}};
print_vector(int_vec);
print_vector(pair_vec);
std::cout << std::endl;
// 3. std::map with structured bindings (C++17)
std::cout << "3. std::map with structured bindings (C++17)" << std::endl;
std::map<std::string, int> fruits = {
{"orange", 8},
{"grape", 2}
};
print_map(fruits);
std::cout << std::endl;
// 4. constexpr value
std::cout << "4. constexpr value" << std::endl;
double circle_area = pi * 10 * 10;
std::cout << "Area of circle with radius 10: " << circle_area << std::endl;
std::cout << std::endl;
// 5. random_vector
std::cout << "5. random_vector" << std::endl;
std::vector<int> random_numbers(5);
random_vector(random_numbers);
print_vector(random_numbers);
std::cout << std::endl;
// 6. readFile with std::string_view (C++17)
std::cout << "6. readFile with std::string_view (C++17)" << std::endl;
// Create a dummy file to read
std::ofstream outfile("test.txt");
outfile << "Hello from test.txt!" << std::endl;
outfile << "This is the second line." << std::endl;
outfile.close();
std::cout << readFile("test.txt") << std::endl;
return 0;
}
실행 결과
1. C-style array and std::array with templates
1, 2, 3, 4, 5
1.1, 2.2, 3.3, 4.4, 5.5
2. std::vector with template specialization
10, 20, 30
apple: 5, banana: 3
3. std::map with structured bindings (C++17)
grape: 2
orange: 8
4. constexpr value
Area of circle with radius 10: 314.159
5. random_vector
-5, 8, -4, -1, 10
6. readFile with std::string_view (C++17)
Hello from test.txt!
This is the second line.
활용팁
- 템플릿과
if constexpr
결합: C++17의if constexpr
를 템플릿 함수 내에서 사용하면, 특정 타입에 대해서만 컴파일되는 코드를 작성할 수 있어 더욱 유연하고 최적화된 템플릿을 만들 수 있습니다. - 다양한 컨테이너에
print
함수 확장:print_vector
와 유사한 템플릿 함수를std::list
,std::deque
등 다른 표준 컨테이너에 대해서도 작성하면 디버깅 시 유용하게 사용할 수 있습니다. - 파일 I/O 성능:
readFile
함수는 간단한 예제이지만, 매우 큰 파일을 다룰 때는 메모리 문제를 피하기 위해 파일을 한 번에 모두 읽는 대신 한 줄씩 또는 청크(chunk) 단위로 읽고 처리하는 것이 좋습니다.
'개발 > C++ (98,03,11,14,17,20,23)' 카테고리의 다른 글
Modern C++ : 직접 구현한 표준 알고리즘, equal, fill_n, iota, copy, accumulate (98, 11) (0) | 2025.09.14 |
---|---|
Modern C++ : std::function (11) (0) | 2025.09.13 |
Modern C++ : std::min, max, equal, any_of, all_of, none_of (98, 11, 17) (0) | 2025.09.12 |
Modern C++ : std::remove, std::sort, std::replace (98, 20) (1) | 2025.09.11 |
Modern C++ : std::transform & std::accumulate (98) (0) | 2025.09.10 |
Modern C++ : std::generate, local static variables (98, 11, 14) (0) | 2025.09.09 |
Modern C++ : Lambda Expressions (11, 14, 17, 20) (1) | 2025.09.08 |
Modern C++ : std::numeric_limits (98, 11, 17) (0) | 2025.09.07 |