7. 2.1 왜 써야하죠?
1. 안정성 : Smart Pointer
2. 성능 : Move Semantics
3. 간결 : auto, lambda
4. 병렬화 : Thread
5. 다양한 기능들
…
8. 2.2 Copy vs Move 성능 비교 테스트 코드
template <typename T>
void measure (T& t) {
T t1(t); // copy
T t2(std::move(t)); // move
}
measure(T);
측정 환경 – online compile & run with option GCC 7.1 (https://en.cppreference.com)
코드 출처 - http://modernescpp.com/index.php/copy-versus-move-semantic-a-few-numbers
9. 2.3 Copy vs Move 성능 비교 결과 (1/2)
SIZE = 10,000
std::array<int, SIZE> myArray; // +1.2배
std::vector<int> myVec(SIZE); // +53.88배
std::deque<int>myDec(SIZE); // +65.60배
std::list<int>myList(SIZE); // +705.31배
std::string myString(SIZE,' '); // +54.95배
MOVE
WIN
10. 2.3 Copy vs Move 성능 비교 결과 (2/2)
SIZE = 10,000
std::map<int,int>myMap;
for (auto i= 0; i <= SIZE; ++i) myMap[i]= i; // +925.01 배
std::unordered_map<int,int>myUMap;
for (auto i= 0; i <= SIZE; ++i) myUMap[i]= i; // 896.42배
MOVE
WIN
11. 2.4 그럼 쓰면 되지 무엇이 문제?
1. 내 컴파일러들은 빌드를 잘해내는가 ?
2. 빌드 후 실행 시 예상대로 동작하는가 ?
3. 지원해야 할 환경들(OS, 컴파일러 등등)에서 모두 동일하게 동작하는가?
4. 기존 utility 들을 대체할 가치가 있는가 ?
5. 실수를 유발할 가능성이 높은 코드로 작성되지 않는가?
6. 코드 가독성이 떨어지지 않는가?
7. 기존 코딩 가이드와 충돌하지는 않는가?
…
=> 우리의 고민을 대신 누군가 해주었다면?
13. 리뷰 과정
1. cxx@chromium.org 로 항목 제안
or https://groups.google.com/a/chromium.org/forum/#!forum/cxx 포스트
2. Chromium community 를 통해 자유롭게 공개 논의
3. 최종 결론에 다다르면 chromium repo 에
https://chromium.googlesource.com/chromium/src/+/master/styleguide/c++/c++11.
html 파일 수정 업데이트
4. https://chromium-cpp.appspot.com/ 에 반영됨
16. 4.1 Smart Pointer (1/6)
Unique Pointers (+11)
std::unique_ptr<new Widget> upw1(new Widget);
std::make_unique (+14)
auto upw2(std::make_unique<Widget>());
std::vector<std::unique_ptr<Widget>> v;
v.push_back(std::make_unique<Widget>());
v.clear(); // Widget 메모리 해제
허용
금지
17. 4.1 Smart Pointer (2/6)
Shared Pointers (+14)
std::shared_ptr<new Widget> spw1(new Widget);
std::shared_ptr 은 제어 블럭이 여러개 생성될 수 있는 이슈가 있음.
chromium 은 base::scoped_refptr 라는 자체 shared pointer 를 사용함
* Weak Pointer 는 아직 논의 중
허용
금지
18. Control Block
T Object
4.1 Smart Pointer (3/6)
Shared Pointers Control Block
허용
금지
Reference Count
Weak Count
Other Data
(e.g., custom deleter,
allocator, etc.)
Ptr to T
Ptr to Control Block
std::shared_ptr<T>
19. 4.1 Smart Pointer (4/6)
Shared Pointer 의 여러번 Control Block 생성되는 이슈 발생
auto rpw = new Widget;
std::shared_ptr<Widget> spw1(rpw);
std::shared_ptr<Widget> spw2(rpw);
허용
금지
rpw pointer
control block
ref count = 1
spw1 spw2
control block
ref count = 1
20. 4.1 Smart Pointer (5/6)
shared_ptr control block 이 여러번 생성되는 이슈 회피 방법
- raw pointer 변수로부터 shared_ptr가 생성되지 않도록 함
std::shared_ptr<Widget> spw1(new Widget);
auto spw2 = std::make_shared<Widget>();
허용
금지
21. 4.1 Smart Pointer (6/6)
chromium 이 사용하는 base::scoped_refptr 은?
class 내부에 ref count 존재
- 해당 클래스가 여러 쓰레드에서 접근된다면
- 해당 클래스가 하나의 쓰레드에서만 접근된다면
허용
금지
std::atomic_int ref_count_;
RefCountedThreadSafeBase
+AddRef
+Release
RefCountedThreadSafeClass A
uint32_t ref_count_;
RefCountedBase
+AddRef
+Release
RefCountedClass B
22. 4.2 Move Semantics (1/10)
RValue References (+14)
T(T&& t)
T& operator=(T&& t)
template <typename T> void Function(T&& t) { ... }
이동 생성자 / 이동 연산자 / perfect forward 사용 권장
만약 overloading 을 줄이기 위한 용도로 사용한다면, 예상하지 않은 값은 처리해야함.
<template typename T,
typename = std::enable_if_t<!std::is_base_of<Person, std:decay_t<T>::value &&
!std::is_integral<std::remove_reference_t<T>>::value>>
explicit Person(T&& n) : name(std::forward<T>(n); {
static_assert(std::is_constructible<std::string, T>::value, “”Parameter…………..”);
} *출처 : Modern Effective C++ 저서
허용
금지
아~ 복잡해~
23. 4.2 Move Semantics (2/10)
Move Semantics (+11)
std::move()
Rvalue ref 로 캐스팅. 효율적 이동 연산을 위해 사용
Forwarding references (+11)
std::forward()
Lvalue ref 는 Lvalue Ref, Rvalue ref 는 Rvalue Ref 로 캐스팅. 완벽 전달자
허용
금지
24. 4.2 Move Semantics (3/10)
● 내 클래스가 Move ctor / Move assign 가지는 경우
○ 아무런 코드 추가 없이 컴파일러가 알아서 자동 생성
○ 클래스내 Move ctor / Move assign 선언에 = default; 사용하여 컴파일러가 구현부
생성
○ 클래스내 Move ctor / Move assign 선언 및 구현부 추가
● 컴파일러에 의한 이동 연산자에 대한 기본 함수 생성 조건
○ 클래스에 Copy ctor / Copy assign 선언이 없어야 함
○ 클래스에 Move ctor / Move assign 선언이 없어야 함
○ 클래스에 소멸자 선언이 없어야 함
허용
금지
25. 4.2 Move Semantics (4/10)
Default Function Creation (+11)
Function(arguments) = default;
컴파일러가 기본 함수 생성하도록 명시
Function Suppression
Function(arguments) = delete;
이동은 허용하지만 복사는 허용하지 않는다면, 하기 같은 매크로 이용하면 좋을 듯
#define DISALLOW_COPY_AND_ASSIGN(TypeName)
TypeName(const TypeName&) = delete;
TypeName& operator=(const TypeName&) = delete
허용
금지
26. 4.2 Move Semantics (5/10)
Emplacement methods for containers (+11)
emplace(), emplace_back(), emplace_front(), emplace_hint()
삽입 push_back 보다 생성삽입 emplace_back 가 성능이 좋을 수 있음.
Emplacement 또다른 장점은 한번에 여러개 인수 전달 가능
void emplace_back(Args&&...);
v.emplace_back(“a”, “b”, “c”);
=> push_back 은?
v.push_back(“a”); v.push_back(“b”); v.push_back(“c”);
허용
금지
27. 4.2 Move Semantics (6/10)
push_back vs emplace_back
std::vector<std::string> v;
v.push_back(“abc”); // 임시객체 String 생성 -> move 생성자 -> 임시객체 소멸
v.emplace_back(“abc”) // char* 그대로 전달 -> String 생성 삽입
String s(“abc”);
v.push_back(s); // 성능 차이 없음
v.emplace_back(s); // 성능 차이 없음
허용
금지
28. 4.2 Move Semantics (7/10)
noexcept Specifier (+11)
void f() noexcept;
compiler 에게 최적화할 수 있는 기회 제공 (호출 스택이 풀릴 여지가 없다라고 과정하기 때문)
move constructor 에서 적극적 사용 권장
=> std::vector::push_back 을 포함한 여러 표준 라이브러리 함수에서 move 연산이 수행됨
* move 연산자에 noexcept 명시자가 없으면, exception 이 발생할 수 있는 코드라 간주, 이전
상태로 돌릴 수 있는 복사 연산을 선택함.
Chromium 사용 예제)
GURL(GURL&& other) noexcept;
GURL& operator=(GURL&& other) noexcept;
허용
금지
29. 4.2 Move Semantics (8/10)
Move Iterator Adaptor (+11)
std::make_move_iterator()
반복문을 통해 객체들을 copy 대신 move 처리
std::unique_ptr 같은 move only 타입들을 담고 있는 container 들 이동할 때 유용함
허용
금지
30. 4.2 Move Semantics (9/10)
Move Iterator Adaptor(+11) 사용 예제
std::list<std::string> s{"one", "two", "three"};
std::vector<std::string> v1(s.begin(), s.end()); // copy
std::vector<std::string> v2(std::make_move_iterator(s.begin()),
std::make_move_iterator(s.end())); // move
v1, v2 는 "one", "two", "three"
s 는 empty
허용
금지
31. 4.2 Move Semantics (10/10)
Ref-qualified Member Functions (+11)
class T {
void f() & {}
void f() && {}
};
Chromium 의 경우) 지나치게 모호한 성격을 띄기 때문에 OWNERS 의 승인 후 사용
가능
T t;
t.f(); // first
T().f(); // second
std::move(t).f(); // second
허용
금지
32. 4.3 Type Deduction (1/3)
Automatic Types (+11)
auto a = 1 + 2;
readability 를 고려해서 사용
- raw pointer 에서는 auto* 를 사용 (auto 가 smart pointer 인지 raw pointer 인지 알수가 없음)
Trailing Return Types (+11)
auto function declaration -> return_type
readability 가 좋은 곳에서만 사용할 것
허용
금지
33. 4.3 Type Deduction (2/3)
Declared Type Accessor (+11)
decltype(expression)
bool f(const Widget& w)
auto w1 = w; // auto 는 Widget 로 deduction 됨
decltype(w) // decltype 은 const Widget& 으로 deduction 됨
허용
금지
34. 4.3 Type Deduction (3/3)
Function return type deduction (+14)
auto f() { return 42; }
decltype(auto) g() { return 42; }
Chromium 의 경우) abstract template code 주요 사용할 것을 권장
- decltype(auto) 는 템플릿된 forwarding/wrapper function 에서 사용
template <typename T> decltype(auto) Unwrap(T&& o) {
return Unwrapper<T>::Unwrap(std::forward<T>(o));
}
- 이외의 경우 auto 를 사용
template <typename F> auto BindLambdaForTesting(F&& f)
허용
금지
35. 4.4 Lambdas (1/5)
Lambda expressions (+11)
[captures](params) -> ret { body }
default captures ([=], [&]) 에서 조심하게 사용해야함.
Chromium 의 경우) 다음과 같이 제약함
- 스택 프레임 밖 lifecycle 를 가지는 로직에서는 어떤 capturing 값도 bind 하거나,
store 하지 않아야 함
- 만약 캡쳐가 필요하면 base::Bind 로 감싸서 base::Callback을 등록하여 사용.
- 캡쳐가 없더라도 base::Bind 와 함께 사용 가능
허용
금지
36. 4.4 Lambdas (2/5) - Chromium 사용 예제
• 캡쳐도 없고 스택 프레임 안에서 동작하는 경우
std::sort(v.begin(), v.end(), [](int x, int y) {
return Weight(x) < Weight(y);
});
• Lambda 를 base::Bind 로 감싼 경우
Return base::Bind(
[](Status* status, bool accepted) {
*status = accepted ? Status::ACCEPTED : Status::CANCELLED;
}, status);
• this 를 캡쳐해야하고 스택 프레임 밖에서(또는 다른 쓰레드에서) 동작하는 경우
io_task_runner_->PostTask(
FROM_HERE, base::Bind(&ExternalDataUseObserver::OnDataUseBatchComplete,
GetWeakPtr()));
37. 4.4 Lambdas (3/5)
Bind Operations
std::bind(function, args, ...)
대신 base::Bind 사용 => Lifecyle 관리에 유용함
허용
금지
base::Bind(&Widget::Function, GetWeakPtr()); std::weak_ptr<Widget> thisWeakPtr(shared_from_this());
std::bind(&StaticFuntion, thisWeakPtr);
staticFunction(std::weak_ptr<Widget> thisWeakPtr) {
Widget w = thisWeakPtr.lock();
if (!w) return;
w->Function();
}
38. 4.4 Lambdas (4/5)
Function Objects
std::function
대신 base::Callback 사용
- Class 의 refcounting 과 weak ptr 로 지원됨
허용
금지
39. 4.4 Lambdas (5/5)
Generic lambdas (+14)
[](const auto& x) { ... }
람다 argument type 을 위한 auto 사용
허용
금지
40. 4.5 Thread (1/3)
thread Libraries (+11)
<thread> <future> <mutex>, <condition_variable>..
Chromium 의 경우)
- base::Thread 가 MessageLoop 와 이미 밀접하게 붙어있어 대체하기 어렵다 판단
- locking/synchronization 같은 기존 클래스를 표준 mutex, unique_lock 등으로 교체
가능한지 검토 예정
허용
금지
41. 4.5 Thread (2/3)
Atomics (+11)
std::atomic<types>
for lockless concurrent programming
원자적 연산 가능 (RMW (Read/Modify/Write)
복사나 배정 불가능.
=> load() 와 store() 멤버 함수들 통해 배정 지원.
memory-ordering 명시 가능.
: load/store 에 명시된 메모리 순서에 따라,
쓰기 순서를 보장하거나, 쓰여지면 읽기를 수행하는 등
메모리 접근에 대한 순서를 명시할수 있음
허용
금지
Thread A
Thread B
Integer 10
read read
10
10
10 + 1 = 11
increment
10 + 1 = 11
increment
11 11
write write
time
42. 4.5 Thread (3/3)
thread_local storage class (+11)
thread_local int foo = 1;
Chromium 의 경우) WTF::ThreadSpecific 을 사용했을 때 보다 성능 측면에서 2.5x 배
빠름
그러나 Android M 이전 버전에서 이상 동작.
Mac 에서 TLS object 가 destroy 된 이후 재 접근시 새로 생성되는 이슈 있음.
TLS object 가 POD type 이고 re-initialize 가 되어도 상관 없으면 사용해도 됨.
Chromium 의 경우) /base/threading/sequence_local_storage_slot.h 사용 권장
허용
금지
43. 4.6 다루지 못한 것들
1. 나머지 금지 항목들 / 허용하지만 조심히 다루어야 할 항목들
=> Appendix 참조
2. 나머지 허용 항목들 / 아직 논의 중인 항목들
자세한 내용들은 https://chromium-cpp.appspot.com/ 참조
45. 5. Conclusion
1. Modern C++ 사용하자
- 성능 향상, 다양한 기능, 안정성, 간결한 코딩
2. 사용전에 지원 환경/동작 유무 등을 모두 검토하자
3. feature 특징에 따라 조심해서 사용하자
4. 내 프로젝트에도 C++ Coding Guide 를 세워보자
5. Chromium base library 참고해볼만하다
50. A1. 이외 금지 (1/8)
Inline Namespaces (+11)
namespace outer {
inline namespace v2 { void Func() { return std::string("v2 Func"); } }
namespace v1 { void Func() { return std::string("v1 Func"); } }
} // namespace outer
인라인 네임스페이스의 멤버는 부모 네임스페이스의 멤버로 처리됨.
즉 outer::v2::Func 가 아니라 outer ::Func 로 직접 호출할 수 있음
큰 버전 정책에 용이하나, 불명확한 namespace 사용으로 혼란스러움.
허용
금지
51. A1. 이외 금지 (2/8)
long long (+11)
64 bits integer
third party code 와 호환하기 어려움
<stdint.h> 내에 int16_t, uint32_t, int64_t 같은 타입을 사용
허용
금지
52. A1. 이외 금지 (3/8)
User-Defined Literals (+11)
long double operator "" _w(long double);
unsigned operator "" _w(const char*);
1.2_w; // calls operator "" _w(1.2L)
12_w; // calls operator "" _w("12")
구글 c++ style 가이드에서 금지
경험이 많은 C++ 개발자에게도 낯선 새로운 구문 형태는 혼란을 가중시킴
허용
금지
53. A1. 이외 금지 (4/8)
Ratio Template Class (+11)
std::ratio<numerator, denominator>
std::cout << ratio_<1,2> + ratio_<1,3> << std::endl;
Output : ⅚
google c++ style 에서 금지됨
해당 템플릿이 더 많은 템플릿 인터페이스들과 연결되어 있어 우려됨
허용
금지
54. A1. 이외 금지 (5/8)
C Floating-Point Environment (+11)
<cfenv>, <fenv.h> header
부동 소수점 상태 플래그 및 C 호환성을 위한 제어 모드 제공
google c++ style 가이드에서 금지됨
많은 컴파일러들이 제대로 지원하고 있지 않음
허용
금지
55. A1. 이외 금지 (6/8)
Regular Expressions (+11)
<regex>
별다른 논의는 없었음.
Chromium내에 많은 regular expression libraries 가 기존재함으로 기존 것 사용.
혹은 thirdparty/re2 (by google) 사용
허용
금지
56. A1. 이외 금지 (7/8)
std::chrono literals (+14)
<chrono> header & std::chrono literals
using namespace std::chrono_literals;
auto timeout = 30s;
Date and time utilities
별다른 논의는 없었으나 base/Time 유지하도록 함
허용
금지
57. A1 이외 금지 (8/8)
Exceptions
<exception>
google c++ style 가이드에서 금지되어 있음
기존 코드에 적용하는 것보다 새로운 프로젝트에 사용하는게 적절함
exception 이 적용된 프로젝트를 기존 exception-free 코드에 적용하면 통합에 문제가
발생할 수 있음.
허용
금지
58. A2. Alignment
Alignment Features (+11)
struct alignas(64) Empty64 {}; alignof(Empty64) => 64
alignas 는 export / #pragma pack 에서 호환되지 않음.
- ALIGNAS() 대신 사용 (base/compiler_specific.h)
Aligned storage (+11)
std::aligned_storage<len, align>
MSVC 2017 에서 align 8 byte 이상 값을 처리 못함
Alignment Features 대신 사용
std::aligned_storage<10, 64> => alignas(64) char data[10];
허용
금지
59. A3. Allow, But Be careful
Non-Static Class Member Initializers (+11)
class C {
type var = value;
C() // copy-initializes var
}
기본 사용 허용. 단, 하기 경우 제외
생성자 안에서 다른 멤버 초기화를 위해 필요한 경우
초기화가 복잡한 경우
허용
금지
60. A3. Allow but Be careful
Standard Integers (+11)
Typedefs within <stdint.h> and <inttypes>
int16_t, uint32_t, int64_t…. 등 size safe 한 타입 사용 권장
short, unsigned long long 도 허용, 단 size 에 대해 명확할 경우에만.
unsigned 보다는 signed type 을 사용할 것
- unsigned 는 bit 표현을 위한 목적일 때만 사용
- negative 값이 절대 없다고 가정하여 unsigned를 사용하지는 말 것.
signed 를 사용하되 Assertion으로 처리할 것
허용
금지
61. A3. Allow but Be careful
Raw string literals (+11)
string var=R"(raw_string)";
GCC 4.9+ 보다 오래된 버전이라면, lexer(=lexical analyzer) bug 존재
=> new line 이 포함된 literals 일 때 이슈 있음
#pragma omp parallel num_threads(sizeof R"(
abc
)")
허용
금지
62. A3. Allow but Be careful
Type Aliases ("using" instead of "typedef") (+11)
using new_alias = typename
typedef 대신 사용
C 와 호환되어야 할 헤더의 경우 제외
허용
금지
63. A3. Allow but Be careful
Constant Expressions (+11)
constexpr int f();
constexpr bool b1 = noexcept(f());
Relaxed constant expressions (+14)
constexpr 함수에서 return 이외 조건문 추가 등 및 복잡한 구현 가능
VS 2017 constexpr 함수내 loop 사용 지원됨 (17년 9월말)
변수에서는 const 보다 constexpr 를 사용, 함수에서는 조심스럽게
- 복잡성을 가지는 함수에서는 사용을 피할 것.
- 함수를 inline 으로 강제하기 위한 목적으로 사용하지 말 것
허용
금지
64. A3. Allow but Be careful
Uniform Initialization Syntax (+11)
type name {[value ..., value]};
심플한 초기화를 위해서는 assignment syntax 사용
std::pair<bool, double> p = {true, 2.0};
std::vector<std::string> v = {"one", "two", "three"};
특별한 로직, 명시적 생성자, reader 에게 직관적으로 심플하지 않을 때 constructor syntax 사용
MyClass c(1.7, false, "test");
std::vector<double> v(500, 0.97); // Creates 500 copies of the provided initializer
uniform init syntax 사용. (= 없는 {} 사용)
C c{true}; // Cannot use '=' since C() is explicit (and "()" is invalid syntax here)
auto 와 섞어서 쓰지 말 것
- auto x{1} => 의도가 int 로 보이지만 std::initializer_list<int> 로 동작됨)
허용
금지