#29. [모던 C++] 기타(C++11, C++17, C++20)
- (C++11~) alignas() 와 alignof()가 추가되어 메모리 정렬 방식을 표준화 됐습니다.
- (C++11~) 가변 매크로가 추가되어 C언어와의 호환성이 높아졌습니다.
- (C++11~) 멤버의
sizeof()
시 동작이 개선되어 개체를 인스턴스화 하지 않더라도 개체 멤버의 크기를 구할 수 있습니다.- (C++17~) __has_include가 추가되어 #include 하기 전에 파일이 존재하는지 확인할 수 있습니다.
- (C++20~) volatile의 일부가 deprecate되었습니다.
- (C++20~)
__VA_OPT__
가 추가되어 가변 인자가 있을 경우에는 괄호 안의 값으로 치환하고, 없을 경우에는 그냥 비워둡니다.- (C++20~) __has_cpp_attribute() 매크로 함수가 추가되어 C++11부터 추가된 특성(attirbute)이 지원되는지 확인 할 수 있습니다.
- (C++20~) 언어 지원 테스트 매크로가 추가되어 컴파일러가 C++11부터 추가된 언어 기능을 지원하는지 테스트 할 수 있습니다.
(C++11~) alignas(), alignof()
기존에는 #pragma pack
을 이용하여 비표준 방식으로 메모리 정렬을 했는데요(개체 크기와 메모리 정렬 참고),
C++11 부터는 alignas() 와 alignof() 로 이를 표준화하였습니다.
항목 | 내용 |
---|---|
alignof() (C++11~) |
주어진 타입의 메모리 정렬 크기를 구합니다. |
alignas() (C++11~) |
주어진 값으로 메모리 정렬을 합니다. 단, 2, 4, 8, 16… 단위로 정렬하며, 내부 멤버중 제일 큰 값보다 작으면 무시합니다. |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 4byte 단위로 정렬합니다.
class alignas(alignof(int)) A_11 {
char m_A;
int m_B;
};
// 2, 4, 8, 16... 단위로 정렬하며, 내부 멤버중 제일 큰값보다 커야 합니다.
class alignas(8) B_11 {
char m_A[13];
int m_B;
};
// 2는 내부 멤버중 제일 큰 값인 int 보다 적은값이므로 무시되고 alignof(int) 크기로 정렬됩니다.
class alignas(2) C_11 {
char m_A[13];
int m_B;
};
// 4byte 단위로 멤버 변수가 할당 되므로 char(1) + int(4) = 5 이므로 4 * 2 개 영역에 할당됨
EXPECT_TRUE(alignof(A_11) == 4 && sizeof(A_11) == 4 * 2);
// 8byte 단위로 멤버 변수가 할당 되므로 13 + int(4) = 17 이므로 8 * 3 개 영역에 할당됨
EXPECT_TRUE(alignof(B_11) == 8 && sizeof(B_11) == 8 * 3);
// 4byte 단위로 멤버 변수가 할당 되므로 13 + int(4) = 17 이므로 4 * 5 개 영역에 할당됨
EXPECT_TRUE(alignof(C_11) == 4 && sizeof(C_11) == 4 * 5);
(C++11~)가변 매크로
가변 매크로는 C99 에 도입되었으며, C와의 호환성을 위해 C++11에 추가되었습니다.
가변 인자를 사용하는 함수를 매크로 함수로 호출할 때 사용합니다.
다음 코드에서처럼 매크로 함수 정의시에 ...
을 사용하고, __VA_ARGS__
를 이용하여 전달할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#define MY_SUM_11(count, ...) T::Sum(count, __VA_ARGS__)
class T {
public:
static int Sum(int count, ...) {
int result{0};
std::va_list paramList; // 가변 인자
va_start(paramList, count); // 가변 인자 처리 시작
for (int i{0}; i < count; ++i) {
result += va_arg(paramList, int); // 가변 인자 추출
}
va_end(paramList); // 가변 인자 처리 끝
return result;
}
};
// 가변 인자에 인수가 있는 경우
EXPECT_TRUE(T::Sum(3, 1, 2, 3) == 1 + 2 + 3);
EXPECT_TRUE(MY_SUM_11(3, 1, 2, 3) == 1 + 2 + 3);
하지만, 인수가 없는 경우에는 T::Sum(count, )
로 치환되어 컴파일 오류가 납니다.
1
2
3
// 가변 인자에 인수가 없는 경우
EXPECT_TRUE(T::Sum(0) == 0);
EXPECT_TRUE(MY_SUM_11(0) == 0); // (X) 컴파일 오류. T::Sum(count, ) 로 치환됩니다.
이러한 문제 때문에 ##__VA_ARGS__
가 제공되며, 앞의 ,
를 없애줍니다.
1
2
3
4
5
6
7
#define MY_SUM(count, ...) T::Sum(count, __VA_ARGS__)
#define MY_SUM2_11(count, ...) T::Sum(count, ##__VA_ARGS__)
// 가변 인자에 인수가 없는 경우
EXPECT_TRUE(T::Sum(0) == 0);
EXPECT_TRUE(MY_SUM(0) == 0); // (X) 컴파일 오류. T::Sum(count, ) 로 치환됩니다.
EXPECT_TRUE(MY_SUM2_11(0) == 0); // (O)
(C++11~) 멤버 sizeof() 연산자
멤버의 sizeof()
시 동작이 개선되어 개체를 인스턴스화 하지 않더라도 개체 멤버의 크기를 구할 수 있습니다.
1
2
3
4
5
6
7
8
class T {
public:
int m_X;
};
// (X) C++03 에서는 컴파일 오류
// (O) C++11 부터 허용
EXPECT_TRUE(sizeof(T::m_X) == sizeof(int));
(C++17~) __has_include
파일이 존재하는지 확인하는 전처리기 입니다.
1
2
3
#if __has_include(<optional>)
# include <optional>
#endif
(C++20~) volatile 일부 deprecate
다음 4가지 경우에 대해 volatile 사용이 deprecate되었습니다.
-
복합 대입
1 2 3 4 5 6 7 8
int a, b; volatile int volatile_val; // 복합 대입 deprecate volatile_val = a; // (O) b = volatile_val; // (O) b = volatile_val = a; // (X) volatile_val에 한번 접근하는지 두번 접근하는지 모호
-
산술형 대입 연산, 증감 연산
1 2 3 4 5
volatile int volatile_val; // 산술형 대입 연산자, 증감 연산자 deprecate volatile_val = volatile_val + 1; // (O) volatile_val += 1; // (X) volatile_val에 한번 접근하는지 두번 접근하는지 모호
-
함수 인자와 리턴값
1 2 3
// 함수 인자와 리턴값 deprecate volatile int f(); // (X) void g(volatile int); // (X)
-
구조적 바인딩
1 2 3 4 5 6 7
// 구조적 바인딩 deprecate sturct A { volatile int x; volatile int y; }; A a; auto [x_17, y_17]{a}; // (X)
(C++20~) VA_OPT
__VA_OPT__
가 추가되어 가변 인자가 있을 경우에는 괄호 안의 값으로 치환하고, 없을 경우에는 그냥 비워둡니다.
1
2
3
4
5
int Sum(int init, int a, int b, int c) {return init + a + b + c;}
int Sum(int init) {return init;}
#define MY_FUNC_20(...) Sum(10 __VA_OPT__(,) __VA_ARGS__) // 가변 인수가 있다면 ,를 넣습니다.
EXPECT_TRUE(MY_FUNC_20(1, 2, 3) == 10 + 1 + 2 + 3); // f(10, 1, 2, 3)가변 인수가 있다면 , 를 넣습니다.
EXPECT_TRUE(MY_FUNC_20() == 10); // f(10)가변 인수가 없다면 ,를 넣지 않습니다.
__has_cpp_attribute() 매크로 함수
C++20 부터는 __has_cpp_attribute() 매크로 함수가 추가되어 C++11부터 추가된 특성(attirbute)이 지원되는지 확인 할 수 있습니다.
테스트할 수 있는 특성(attirbute)은 https://en.cppreference.com/w/cpp/feature_test를 참고하시기 바랍니다.
1
2
3
4
5
#if __has_cpp_attribute(deprecated)
std::cout << "Support deprecated" << std::endl; // deprecated를 지원합니다.
#else
std::cout << "No Support deprecated" << std::endl;
#endif
언어 지원 테스트
C++20 부터는 언어 지원 테스트 매크로가 추가되어 컴파일러가 C++11부터 추가된 언어 기능을 지원하는지 테스트 할 수 있습니다.
테스트할 수 있는 항목은 https://en.cppreference.com/w/cpp/feature_test를 참고하시기 바랍니다.
1
2
3
4
5
#if __cpp_char8_t
std::cout << "Support char8_t" << std::endl; // char8_t를 지원합니다.
#else
std::cout << "No Support char8_t" << std::endl;
#endif
(C++20~) STL 지원 테스트 매크로가 추가되어 C++11부터 추가된 STL 기능을 지원하는지 테스트 할 수 있습니다.
댓글남기기