
개인적으로 공부하면서, 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_limits<std::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바이트)로 출력된다.
- 포인터를 그냥 선언해서는 변수값을 담을 수 없다.
> char *name = "nolda"; (X)
변수값을 담기 위해선 const 선언을 넣으면 사용할 수 있다.
> const char *name = "nolda"; (O)
//참고
(*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 Hello
std::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
Lambda (람다 함수) : 익명 함수
auto func = [](const int& i) -> void { cdout << "Hello, World!!" << endl; };
{
string name = "JackJack";
[&]() { std::cout << name << endl; } ();
}
//lambda의introducer인 []에 &을 넣으면, 밖에있는 것을 레퍼런스로 가져올 수 있음. == name
std::function
std::function<void(int)> func3 = func2;
std::function<void()> func4 = std::bind(func3, 456); //반환값 파라미터 bind
Object instance;
auto f = std::bind(&Object::hello, &instance, std::placeholders::_1);
//멤버함수를 instance에 바인딩
//파라미터가 1개이므로 _1, 늘어나면 매개변수 추가(_2, _3, ...)
함수에서 리턴값 여러개 반환하기 (C++17)
#include <tuple>
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;
}
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();
Race Condition - std::atomic, std::scoped_loc
#include <atomic>
//int shared_memory(0);
→ t1 스레드가 더하는 순간 t2가 가로채는 문제 발생할 수 있음.
atomic<int> shared_memory(0); //문제 해결
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::lock_guard lock(mtx);
shared_memory++;
...
std::lock_guard 사용을 권장...
std::scoped_lock lock(mtx);
...
std::scoped_lock 권장작업 기반 비동기 프로그래밍 (Task)
#include <future>
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();
}
}
std::forward - 완벽한 전달
#include <utility>
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<typename T>
void func_wrapper(T t)
{
func(t);
}
↓ Perfect Forwarding
template<typename T>
void func_wrapper(T&& t)
{
func(std::forward<T>(t));
}
int main()
{
MyStruct s;
func_wrapper(s);
func_wrapper(MyStruct());
//func(s);
//func(MyStruct());
}
자료형 추론
auto / template<typename>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; → &를 붙여줌