2025년 9월 9일
개인적으로 공부하면서, C#과 같이 사용하면서 헷갈릴 수 있는 부분(C++과 다른 점)과 C++에서 특정 버전 이상에서만 지원하는 문법을 정리한다.

Modern C++은 C++11 이후 버전을 의미한다.
typedef
typedef double my_type_t;
== using my_type_t = double; (C++11 이상)enum
enum { };
enum class { }; (C++11 이상)
//c#의 enum과 같이 사용하려면 enum class를 사용한다.
//enum은 스코프를 사용하지 않지만 enum class에서는 C#과 마찬가지로 스코프를 사용한다.
string
//c++의 string은 std 라이브러리에 포함된다.
//cin으로 문자열을 온전히 받기 위해선
std::getline(std::cin, {변수명})
//이 사용된다.
//cin으로 입력받은 버퍼를 비우기 위해
std::cin.ignore(std::numeric_limitsstd::streamsize::max(), '\n');
== std::cin.ignore(32767, '\n');
//사용이 필요하다.코드에서 긴급한 탈출 (halt)
HALT → exit(0);랜덤 난수 생성
#include <cstdlib>
std::srand(5323); //5323 = seed → 시드 넘버 지정
std::srand(static_cast<unsigned int>(std::time(0)); // 시간과 연동하여 시드가 계속 변경됨
std::rand();#include <random> (C++11 이상)
std::random_device rd;
std::mt19937_64 mesenne(rd()); // or mt19937
std::uniform_int_distribution<> dice(1 ,6); // 1~6까지 같은 확률cin 활용
# 버퍼 지우기
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
== std::cin.ignore(32767, '\n');
입력 범위 문제 확인하기 (둘이 같이 사용됨.)
std::cin.fail(); // return true / false
std::cin.clear(); // 내부 상태 플래그 초기화배열과 포인터
배열을 정의하면 배열의 이름 자체가 주소가 된다.
다만 배열을 함수의 인자로 넘겨, 매개변수로 배열을 받는다면 이 매개변수는 '포인터'로 취급된다.
때문에 매개변수로 받은 배열의 이름(포인터)은 배열의 주소를 저장하는 다른 주소가 된다.
→ 함수에서 이 (배열로 보이지만 포인터 변수인)배열의 sizeof를 찍으면 4바이트(32비트, 64비트에서는 8바이트)로 출력된다.
포인터를 그냥 선언해서는 변수값을 담을 수 없다.<blockquote>
char *name = "nolda"; (X)
변수값을 담기 위해선 const 선언을 넣으면 사용할 수 있다.
const char *name = "nolda"; (O)
</blockquote>
//참고
(*this).memberValue;
== this->memberValue;foreach
C#의 foreach와 사용법은 비슷하지만 문법의 차이가 존재한다.
for (int value : array)
{
}
메모리 할당
정적으로 할당된 메모리는 stack에 저장되며, stack은 용량이 작고 컴파일 타임에 크기가 결정된다.
동적으로 할당된 메모리는 Heap에 할당된다. Heap 영역은 런타임에 결정된다.Stack / Heap
Memory의 Segment
1. Code - Program
2. BSS - uninitialized data
3. Data - initialized data
4. Stack
- 로컬 변수, 메인 함수, 메소드 등이 스택으로 쌓임
- 사이즈가 작기 때문에 Stack Overflow 발생 가능
5. Heap
- 동적 할당일 경우 Heap 영역에 저장됨
const와 포인터
int value = 5;
const int *ptr1 = &value; //ptr1에 저장돼있는 주소를 바꾸는건 가능하지만, 주소가 가리키는 value 값을 바꾸는건 안됨
int *const ptr2 = &value; //ptr2에 저장돼있는 주소 값 바꾸는게 안됨
const int *const ptr3 = &value; //주소 값도 바꿀 수 없고 de-referencing으로 값을 바꿀 수도 없음 (다 안됨)std::vector
#include <vector>
std::vector<int> array_value;
동적할당 배열에 유용하게 쓰이고 널리 쓰임. C#의 List와 비슷하다.
//size : 사용하는 용량 -> .resize()
//capacity : 총 용량(size에서 가려진 총 용량) -> .reserve
//vector를 stack처럼 사용하기
.push_back();, .pop_back()std::tuple
여러 개의 반환 값을 return 시킬 수 있다.
#include <tuple>
std::tuple<int, double> getTuple()
{
return std::make_tuple(5, 3.14);
}
int main()
{
std::tuple<int, double> tp = getTuple();
std::get<0>(tp); //int 값
std::get<1>(tp); //double 값
//C++17 이상에서는 아래가 가능하다.
auto[a, b] = getTuple();
}함수포인터
int func() { return 5; }
int func2() { return 9; }
//함수포인터 변수 선언
int(*fcnptr)() = func;
-> int(*변수이름)(매개변수)
//다른 함수 할당
fcnptr = func2;
//functional Library (C++11 이상)
#include <functional>
std::functional<리턴타입(매개변수)> fcnptr = func;일립시스 (Ellipsis)
//매개변수의 갯수제한을 두지 않고 받는 방법
double findAverage(int count, ...) //count : 매개변수 갯수
{
double sum = 0;
va_list list;
var_start(list, count);
for (int arg = 0; arg < count; ++arg)
{
sum += va_arg(list, int);
}
var_end(list);
return sum / count;
}연쇄호출(Chaining)
class 내부 함수의 리턴을 class 자신의 reference 값으로 설정하면,
연쇄적으로 호출이 가능하다.
class Calc
{
int m_value;
Calc& Add(int value) { m_value += value; return *this; }
Calc& Sub(int value) { m_value -= value; return *this; }
};
int main()
{
Calc cal(10);
cal.Add(10).Sub(20).Add(10);
}friends keyword
class에서 다른 함수의 선언부를 가져와 friend 선언을 해주면 해당 함수에서는 선언된 class의 private 멤버에 접근할 수 있다.
각자 다른 class에서 공통 함수에 friend를 선언할 경우 전방선언이 필요할 수 있음.
순서에 의해 friend 함수에서 멤버 변수 등을 인식하지 못하면, class에는 선언부만 남겨두고,
인식하지 못한 class의 하단에 구현부를 넣어준다.익명 변수
class A
{
void print()
{
cout << "A Print" << endl;
}
};
int main()
{
A().print();
A().print(); //위의 객체와 다르기 때문에 생성자와 소멸자가 각각 호출됨.
}연산자 오버로딩
//산술 연산자
class Cents
{
private:
int m_cents;
public:
int& getCents() { return m_cents; }
Cents operator + (const Cents &c2) //멤버 함수
{
return Cents(this->m_cents + c2.m_cents);
}
friend Cents operator + (const Cents &c1, const Cents &c2) //friend 함수
{
return Cents(c1.getCents() + c2.getCents());
}
};//입출력 연산자
class Point
{
private:
double m_x, m_y, m_z;
public:
Point(double x = 0.0, double y = 0.0, double z = 0.0)
: m_x(x), m_y(y), m_z(z)
{ }
//outstream
friend std::ostream& operator << (std::ostream &out, const Point &point)
{
out <<<< point.m_x << ", " << point.m_y << ", " << point.m_z;
return out;
}
//instream
friend std::istream& operator >> (std::istream& in, Point& point)
{
in >> point.m_x >> point.m_y >> point.m_z;
return in;
}
};
int main()
{
Point p1(0.0, 0.1, 0.2), p2(3.4, 1.5, 2.0);
Point p3, p4;
cout << p1 << " " << p2 << endl;
cin >> p3 >> p4;
}
//단항 연산자
Cents operator - () const
{
return Cents(-m_cents);
}
bool operator ! () const
{
return (m_cents == 0 ? true : false);
}//비교 연산자
friend bool operator == (const Cents& c1, const Cents& c2)
{
return c1.m_cents == c2.m_cents;
}
friend bool operator < (const Cents& c1, const Cents& c2)
{
return c1.m_cents < c2.m_cents;
}//증감 연산자
Cents& operator ++ () //prefix
{
++m_cents;
return *this;
}
Cents& operator ++ (int) //postfix
{
Cents temp(m_cents);
++(*this);
return *temp;
}//첨자 연산자 []
class IntList
{
private:
int m_list[10];
public:
int& operator [] (const int index)
{
return m_list[index];
}
const int& operator [] (const int index) const
{
return m_list[index];
}
};
int main()
{
IntList list;
list[3] = 10;
//if
IntList *list = new IntList;
list[3] = 10; //X
(*list)[3] = 10; //O
}//형변환
operator int()
{
return m_cents;
}explicit / delete
class Fraction
{
int m_numerator;
int m_denominator;
Fraction(char) = delete; //사용 못하게 막음
(explicit) Fraction(int num = 0, int den = 1)
: m_numerator(num), m_denominator(den)
{ }
};
void doSomething(Fraction frac)
{
cout << frac << endl;
}
int main()
{
doSomething(7); //원래는 컴파일러에서 Fraction(7)처럼 변환해줌
//만약 Fraction 생성자에 explicit 키워드가 앞에 붙는다면 불가능해짐
}class 깊은 복사 주의점
//class의 멤버변수로 char *m_data = nullptr;가 정의되어 있을 때,
//이 class를 기본 복사 생성자를 통해 복사하면 새로운 class instance에도 같은 포인터 주소를 가리킨다.
//이 때, 새로운 class instance가 삭제되면, 소멸자에서 해당 포인터 변수가 가리키는 값을 지워버리게 되고,
//원본의 데이터까지 지워버리는 상황이 발생한다. (얕은 복사)
//이 때문에, 깊은 복사를 위해선 별도의 복사 생성자를 정의해줘야 한다.
MyString(const MyString &source) //복사 생성자 (깊은 복사)
{
m_length = source.m_length;
if (source.m_data != nullptr)
{
m_data = new char[m_length];
for (int i = 0; i < m_length; ++i)
m_data[i] = source.m_data[i];
}
else
m_data = nullptr;
}
}
//operator = (대입 연산자) 의 경우에도 비슷하게 정의해줄 수 있음.
MyString& operator = (const MyString &source)
{
if (this == &source)
return *this;
delete[] m_data; //기존에 갖고있던 메모리 할당 해제
m_length = source.m_length;
if (source.m_data != nullptr)
{
m_data = new char[m_length];
for (int i = 0; i < m_length; ++i)
m_data[i] = source.m_data[i];
}
else
m_data = nullptr;
}
}
//std::string을 사용하면 필요 없는 일이다.
Initializer List 생성자
IntArray(unsigned length)
: m_length(length)
{
m_data = new int[length];
}
IntArray(const std::initializer_list<int> &list)
: IntArray(list.size())
{
int count = 0;
for (auto & element : list)
{
m_data[count] = element;
++count;
}
}상속 관련 Keyword
//virtual
상속 구조에서 부모 클래스의 메소드에 virtual 키워드를 사용시
자식 클래스의 객체를 부모 클래스의 포인터에 넣어서 호출해도
자식클래스의 메소드가 호출됨.
부모 클래스의 함수를 자식 클래스에서 오버라이딩한 것으로 인식
가상 소멸자
부모 클래스 객체에 자식 클래스를 넣을 경우,
자식 클래스의 동적 할당된 메모리를 지우기 위해 소멸자에도
virtual 키워드를 붙여주면 자식 클래스의 소멸자도 실행됨.
//override
상속 구조에서 자식 클래스의 함수 매개변수 끝에 override 키워드 작성시
오버로딩이 아닌 오버라이드를 의도한것이라고 인식하게 하는 키워드
//final
final 키워드 사용시 자식 클래스에서 더 이상 오버라이딩할 수 없음다이아몬드 상속 문제
A라는 부모클래스를 통해 B와 C클래스를 상속받아 생성할 때,
상속 접근제한자에 virtual을 붙여주지 않으면
B와 C클래스 각각 다른 A클래스를 상속받는 문제가 발생할 수 있음.
class B : virtual public A
와 같이 상속받아야 함.Object Slicing
부모 클래스로부터 상속받아 생성된 자식클래스에 새로운 변수가 있는데,
부모 클래스에 자식 클래스 인스턴스를 넣어버린 경우 데이터 슬라이싱 발생
std::vector를 사용하는 경우
std::vector<std::reference_wrapper<부모 클래스>> vec;
을 사용할 수 있다.dynamic cast
Derived d1;
Base *base = &d1;
auto *base_to_d1 = dynamic_cast<Derived1*>(base);
Base로 형변환 됐던 변수를 다시 Derived로 형변환
dynamic_cast의 경우 에러 체크를 통해 에러일 경우 nullptr 반환함. (안전한 형변환)
static_cast는 에러 체크를 하지 않음.Template
//함수 템플릿
template<typename T>
T getMax(T x, T y)
{
return (x > y) ? x : y;
}//클래스 템플릿
<*.h>
template<typename T>
class MyArray
{
private:
int m_length;
T *m_data;
public:
MyArray(int length)
{
m_lenth = length;
m_data = new T [length];
}
void print();
}
...
<*.cpp>
template<typename T>
void MyArray<T>::print()
{
...
}
template void MyArray<char>;
//템플릿 클래스의 멤버함수의 구현부를 cpp 파일로 옮길 경우
explicit instantiation 필요smart pointer: auto_ptr (Regacy)
std::auto_ptr<int> //c++98 ~ c++11까지 존재 c++17부터 제거
//auto_ptr의 구조
template<typename T>
class AutoPtr
{
public:
AutoPtr(T* ptr = nullptr) : m_ptr(ptr)
{ }
~AutoPtr()
{
if (m_ptr != nullptr) delete m_ptr;
}
};
R-value Reference
int x = 5;
const int cx = 5;
//int &&rr1 = x; (X)
//int &&rr2 = cx; (X)
int &&rr3 = 5;std::move
AutoPtr<Resource> res2 = std::move(res1);
//res1이 R-value임을 인식시켜줌
//이렇게 처리한 경우 res1을 사용하지 않는다는 의미template<class T>
void MySwap(T &a, T &b)
{
//Copy constructor
T tmp = a;
a = b;
b = tmp;
//Move Semantics
T tmp { std::move(a) };
a = std::move(b);
b = std::move(tmp);
}
//AutoPtr class에 Move Semantics를 정의했음example)
vector<string> v;
string str = "Hello";
v.push_back(str); //L-value
cout << std << endl; //Hello
cout << v[0] << endl; //Hello
v.push_back(std::move(str)); //R-value
cout << str << endl; // 공백
cout << v[0] << " " << v[1] << endl; // Hello Hellostd::unique_ptr / std::make_unique (주로 사용되는 smart pointer)
#include <memory>
std::unique_ptr<Resource> res(new Resource(10000));auto res1 = std::make_unique<Resource>(5); res2 = res1; (x)
res2 = std::move(res1); (O)void doSomething(std::unique_ptr<Resource> res)
{
}
doSomething(std::unique_ptr<Resource>(new Resource(1000))); (X)
doSomething(std::make_unique<Resource>(1000)); (O)std::shared_ptr / std::make_shared
std::shared_ptr<Resource> ptr1(res);
...
{
std::shared_ptr<Resource> ptr2(ptr1);
std::shared_ptr<Resource> ptr2(res); //이렇게 사용하면 ptr1이 res의 소유권이 다른 데에도 있다는 것을 알 수가 없음
}auto ptr1 = std::make_shared<Resource>(3);std::weak_ptr
const std::shared_ptr<Person> getPartner() const
{
return m_partner.lock(); //std::weak_ptr<Person> m_partner;
}STL(Standard Template Library)
- std::set<T> //집합. 내부 원소가 겹치면 무시
set.insert("Hello");
set.insert("World");
set.insert("Hello");
> "Hello World"
std::multiset<T> // 중복 원소 허용 집합
std::map<key, value> // key값에 sort 돼있음
.first // key 출력
.second // value 출력
std::multimap<key, value> // 중복 키값 허용 map
multimap.insert(std::pair('a', 10)); //Before c++14, pair<char, int>('a', 10)
std::stack
.push // push adds a copy
.emplace // constructs a new object
std::queue
std::priority_queue //sort 해주는 queue, 사용자 지정 클래스를 우선순위 큐로 만들면, 크기 비교 연산자 오버로딩을 해줘야함STL Iterator (반복자)
vector<int> container;
for (int i = 0; i < 10; ++i)
container.push_back(i);
vector<int>::const_iterator itr; //vector<int>::iterator itr;
itr = container.begin();
while(itr != container.end())
{
cout << *itr << " ";
++ itr;
}vector를 list 또는 set등으로 바꿔도 바로 동작함.for (auto itr = container.begin(); itr != container.end(); ++itr)
cout << *itr << " ";for (auto &e : container)
cout << e << " ";std::string / std::wstring
//int 값을 string으로 변환 → 문자열로 처리됨
std::string my_str(std::to_string(4));
//string을 int값으로 변환
int i = std::stoi(my_str);
//std::ostringstream (output) / std::istringstream (input)string.length()
string.size()
string.capacity() //용량
string.max_size() //최대 크기
string.reserve(1000) //용량 확보 (최소 용량)string my_str("abcdefg");
try
{
my_str[100] = 'X'; → 예외처리 X
my_str.at(100) = 'X'; → 예외처리 O
}
catch { }my_str.c_str() == .data() → C 스타일로 사용할경우 마지막에 null값 ('\0') 포함.append() → string의 끝에 문자열 붙이기istream (Input Stream)
#include <iomanip>
char buf[10]
cin >> setw(5) >> buf; //최대 5글자만 받도록 해줌. <iomanip> include 필요cin.get(var)을 사용cin.get()으로 읽은 후, cin.gcount()를 통해 몇글자를 읽었는지 확인 가능cin.getline()은 라인 단위로 읽음. → 줄바꿈 문자까지 같이 읽어짐 ('\n')string buf;
getline(cin, buf);cin.ignore()는 한 글자를 무시한다.cin.peek()은 버퍼에서 꺼내지 않고, 다음에 올 글자를 확인함cin.unget()은 마지막에 읽은 문자를 버퍼로 다시 넣음cin.putback()은 원하는 글자를 버퍼에 넣음ostream (Output Stream)
cout.setf(std::ios::showpos)는 기호 (+, -)를 숫자 앞에 표시한다.cout.unsetf()은 위의 플래그 삭제cout.setf(std::ios::hex, std::ios::basefield)는 16진수로 출력 == cout << std::hex;cout.setf(std::ios::uppercase)는 16진수의 영문자를 대문자로 표시cout << std::boolalpha를 통해 bool 값 출력cout << std::setprecision(3)은 소숫점 자릿수 설정cout << std::fixed는 소숫점 자릿수 고정cout << std::scientific은 부동 소수점 방식 표기법cout << std::showpoint 소수점 '.' 표기cout << std::setw(10) << std::left(right / internal) << -12345 << endl;
//출력 정렬cout.fill("*") 빈 칸을 별로 채워줌sstream (String Stream)
#include <sstream>
stringstream os;
os << "Hello World"; // 버퍼에 덧붙임
os.str("Hello World"); //버퍼 치환
string str;
str = os.str(); //os.str(""); 파라미터로 공백을 넣으면 치환됨
os >> str;
cout << str;stream state
void printStates(const std::ios& stream)
{
stream.good();
stream.eof();
stream.fail();
stream.bad();
}void printCharacterClassification(const int& i)
{
bool(std::isalnum(i));
bool(std::isblank(i));
bool(std::isdigit(i));
bool(std::islower(i));
bool(std::isupper(i));
// → return 값이 int이므로 bool로 캐스팅
}Regular Expressions (정규 표현식)
#include <regex> // C++11 부터 지원
regex re("\d"); //digit 1개
== regex re("[[:digit:]]{1}");
regex re("\d+"); //1개 이상의 숫자
regex re("[ab]"); //a, b만
regex re("[A-Z]{1, 5}"); //1개 이상 5개 이하의 A-Z 문자
regex re("([0-9]{1})([-]?)([0-9]{1,4})"); // 0-9 숫자 1개 + '-'이 있어도 되고 없어도됨 + 0-9 숫자 1개이상 4개 이하
//regex_match를 통해 매치되는지 판별
string str;
getline(cin, str);
if (std::regex_match(str, re))
cout << "match" << endl;
else
cout << "No match" << endl; //매치되는 것만 출력
auto begin = std::sregex_iterator(str.begin(), str.end(), re);
auto end = std::sregex_iterator();
for (auto itr = begin; itr != end; ++itr)
{
std::smatch match = *itr;
cout << match.str() << " ";
}
cout << endl;fstream (File Stream - 파일 입출력)
#include <fstream>//ASCII code - outputstream
ofstream ofs("my_first_file.dat"); // ostream ofs("my_first_file.dat", ios::app) → append mode : 데이터를 추가
//ofs.open("my_first_file.dat");
ofs << "File Detail" << endl;
//ofs.close() → 영역을 벗어나면 소멸자가 닫아줌. 수동으로 처리할 필요 X//ASCII code - inputstream
ifstream ifs("my_first_file.dat");
while (ifs)
{
std::string str;
getline(ifs, str);
std::cout << str << endl;
}//Binary code - outputstream
const unsigned num_data = 10;
ofs.write((char*)&num_data, sizeof(num_data)); //데이터 개수 정의
for (int i = 0; i < num_data; ++i)
ofs.write((char*)&i, sizeof(i));//Binary code - inputstream
unsigned num_data = 0;
ifs.read((char*)&num_data, sizeof(num_data)); //데이터 개수 확인
for (unsigned i = 0; i < num_data; ++i)
{
int num;
ifs.read((char*)&num, sizeof(num));
std::cout << num << endl;
}ifstream ifs("my_file.txt");
ifs.seekg(5); //5바이트 이동 후 읽기 시작
ifs.seekg(5, ios::cur); //이전에 이동했던 위치에서 5바이트 더 이동 후 읽기 시작
ifs.seekg(0, ios::end); //끝에서 0번째 (마지막 위치)
ifs.tellg(); //현재 위치fstream iofs(filename);
iofs.seekg(5); cout << (char)iofs.get() << endl; //read
iofs.seekg(5); iofs.put('A'); //write
<hr/>
Lambda (람다 함수) : 익명 함수
auto func = [](const int& i) -> void { cdout << "Hello, World!!" << endl; };
{
string name = "JackJack";
<a href="">&</a> { std::cout << name << endl; } ();
}
//lambda의introducer인 []에 &을 넣으면, 밖에있는 것을 레퍼런스로 가져올 수 있음. == name
<hr/>
std::function
std::function<void(int)> func3 = func2;
std::function
Object instance; auto f = std::bind(&Object::hello, &instance, std::placeholders::_1); //멤버함수를 instance에 바인딩 //파라미터가 1개이므로 _1, 늘어나면 매개변수 추가(_2, _3, ...)
<hr/>
함수에서 리턴값 여러개 반환하기 (C++17)
#include
auto my_func() { return tuple(123, 456, 789); }
int main() { auto [a, b, c, d] = my_func();
std::cout << a << " " << b << " " << c << " " << d << endl;
return 0;}
<hr/>
std::thread - 멀티 스레딩 (C++11)
std::thread t1 = std::thread({
while (true)
{ }
});
t1.join(); //thread가 있는데, main이 끝나버릴 수 있으므로 t1이 끝날 때까지 대기해줌
- 스레드가 여러개 있다면, 동시에 실행된다.
mutex mtx; //mutual exclusion (상호 배제)
auto work_func = [](const string& name) { for (int i = 0; i < 5; ++i) { std::this_thread::sleep_for(std::chrono::milliseconds(100));
mtx.lock();
cout << name << " " << std::this_thread::get_id() << " is working " << i << endl;
mtx.unlock();
}};
std::thread t1 = std::thread(work_func, "JackJack"); std::thread t2 = std::thread(work_func, "Dash");
t1.join(); t2.join();
<hr/>
Race Condition - std::atomic, std::scoped_loc
#include
//int shared_memory(0);
→ t1 스레드가 더하는 순간 t2가 가로채는 문제 발생할 수 있음.
atomic
int main() { auto count_func = { for (int i = 0; i < 1000; ++i) { this_thread::sleep_for(chrono::milliseconds(1)); shared_memory++; // == shared_memory.fetch_add(1); } };
thread t1 = thread(count_func);
thread t2 = thread(count_func);
t1.join();
t2.join();
cout << "After" << endl;
cout << shared_memory << endl;}
- 다른 스레드가 메모리를 가로채는 문제
- std::atomic뿐만 아니라 mutex를 사용할 수도 있음.
- atomic 남용시 성능 하락 우려 존재
...
std::lock_guard lock(mtx); shared_memory++; ...
- lock 후 unlock을 할 수 없을 때도 있으므로, `std::lock_guard` 사용을 권장
...
std::scoped_lock lock(mtx); ...
- (C++17 이상) `std::scoped_lock` 권장
<hr/>
작업 기반 비동기 프로그래밍 (Task)
#include
int main() { { //multi-threading int result; std::thread t([&] {result = 1 + 2;} ); t.join(); std::cout << result << std::endl; }
{ //task-based parallelism
auto fut = std::async([] {return 1 + 2;});
std::cout << fut.get() << std::endl;
}
{ //future and promise
std::promise<int> prom;
auto fut = prom.get_future();
auto t = std::thread([](std::promise<int>&& prom)
{
prom.set_value(1 + 2);
}, std::move(prom));
cout << fut.get() << endl;
t.join();
}}
- async는 join()으로 기다리지 않아도 된다
<hr/>
std::forward - 완벽한 전달
#include
struct MyStruct {};
void func(MyStruct& s) { cout << "Pass by L-ref" << endl; }
void func(MyStruct&& s) { cout << "Pass by R-ref" << endl; }
//Template을 사용하면 L-value와 R-value 구분을 못한다.
template
↓ Perfect Forwarding
template
int main() { MyStruct s;
func_wrapper(s);
func_wrapper(MyStruct());
//func(s);
//func(MyStruct());}
<hr/>
자료형 추론
- `auto` / `template<typename>`
- auto는 변수의 const와 &, volatile를 모두 떼버린다.
- `const auto& auto_crx2 = crx`, `volatile auto vavx = vs` 와 같이 선언해야 한다.
- `decltype` (== typeof)
typedef decltype(lhs ** rhs) product_type;
product_type prod2 = lhs * rhs; == decltype(lhs * rhs) prod3 = lhs * rhs;
typedef decltype(x) x_type;
typedef decltype((x)) x_type; → &를 붙여줌 ```