#3. [모던 C++] 사용자 정의 리터럴(C++11, C++20)
- (C++11~) 사용자 정의 리터럴이 추가되어
int operator ""_km(long double val);
와 같이 사용자가 정의해서 사용할 수 있으며, 단위계 처리가 쉬워졌습니다.- (C++20~) 사용자 정의 리터럴 인자 규칙에 char8_t이 추가되었습니다.
사용자 정의 리터럴
C++11 부터는 operator""_식별자()
를 이용하여 사용자가 리터럴을 직접 정의할 수 있습니다. 동일한 값을 여러 단위계로 표현할 때 유용합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// mm 단위로 리턴
int operator ""_km(long double val) {return std::round(val * 1000. * 1000.);}
int operator ""_m(long double val) {return std::round(val * 1000.);}
int operator ""_cm(long double val) {return std::round(val * 10.);}
int operator ""_mm(long double val) {return std::round(val);}
int operator ""_mm(unsigned long long val) {return val;} // 정수형 리터럴도 오버로딩
EXPECT_TRUE(1.5_km == 1500.0_m);
EXPECT_TRUE(1.5_m == 150.0_cm);
EXPECT_TRUE(1.5_cm == 15.0_mm);
EXPECT_TRUE(1.5_mm == 1.5_mm); // 반올림 하므로 2._mm와 동일합니다.
EXPECT_TRUE(1_km == 1000.0_m); // (X) 컴파일 오류. long double만 오버로딩 되었습니다.
EXPECT_TRUE(1_mm == 1); // mm는 정수형도 오버로딩 되었습니다.
(C++14~) 표준 사용자 정의 리터럴이 추가되어
operator ""s
,operator ""min
,operator ""if
, 등 문자열, 날짜 / 시간, 복소수 관련 표현이 간편해 졌습니다.
문자열 추론
기존 문자열을 auto로 초기화 하면 const char*
로 추론되는데요, 사용자 정의 리터럴을 만들어 string으로 추론되게 할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
// const char* 형의 문자열을 std::string으로 변환
std::string operator ""_forced_string(const char* str, size_t size) {
std::cout << "String Size : " << size << std::endl; // 전달된 문자열의 길이입니다.(널문자는 포함되지 않습니다.)
return std::string(str);
}
auto chars_11 = "test"; // const char*
// EXPECT_TRUE(chars_11.size() == 4); // (X) 컴파일 오류. const char*이므로 size() 멤버 함수가 없습니다
auto str_11 = "test"_forced_string; // std::string
EXPECT_TRUE(str_11.size() == 4); // (O)
EXPECT_TRUE("hello"_forced_string.size() == 5); // 임시 개체도 가능합니다.
(C++14~) 표준 사용자 정의 리터럴이 추가되어
operator ""s
,operator ""min
,operator ""if
, 등 문자열, 날짜 / 시간, 복소수 관련 표현이 간편해 졌습니다. 상기와 같은 경우"test"s
로 사용하면 됩니다.
식별자 규칙
식별자는 밑줄(_
)로 시작해야 하며, 이중 밑줄은 허용하지 않습니다.
인자 규칙
사용할 수 있는 인자의 형태는 다음과 같습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 정수 1_a
ReturnType operator ""_a(unsigned long long);
// 실수 1.0_b
ReturnType operator ""_b(long double);
// 문자 'a`_c
ReturnType operator ""_c(char);
ReturnType operator ""_d(wchar_t);
ReturnType operator ""_e(char16_t);
ReturnType operator ""_f(char32_t);
// 문자열 "abc"_g
ReturnType operator ""_g(const char*, size_t);
ReturnType operator ""_h(const wchar_t*, size_t);
ReturnType operator ""_i(const char16_t*, size_t);
ReturnType operator ""_j(const char32_t*, size_t);
// Raw : 3.14_k 처럼 정수나 실수 형태로 전달하고, 전달된 인자는 문자열 3.14로 전달됨
ReturnType operator ""_k(const char*);
// template
template<char...> ReturnType operator ""_l();
문자열과 Raw 타입이 모두 const char*
를 사용해서 헷갈리는데요,
"3.14"_a
와 같이 문자열 상수처럼 호출하면,int operator ""_a(const char* str, size_t size)
이 호출되고,3.14_a
와 같이 정수나 실수 형태로 사용하면int operator ""_a(const char* str)
가 호출됩니다.
1
2
3
4
5
6
7
8
9
int operator ""_a(const char* str, size_t size) {
return 1;
}
int operator ""_a(const char* str) {
return 2;
}
EXPECT_TRUE("3.14"_a == 1); // int operator ""_a(const char* str, size_t size) 버전이 호출됩니다.
EXPECT_TRUE(3.14_a == 2); // int operator ""_a(const char* str) 버전이 호출됩니다.
Raw 타입은 우선 순위가 낮습니다. 만약 const char*
와 long double
이 같은 식별자로 정의되었다면, long double
이 사용됩니다. 따라서 Raw 타입은 다른 타입과 동일한 식별자를 사용하지 말아야 합니다.
1
2
3
4
5
6
7
int operator ""_a(const char* str) { // (△) 비권장. 우선 순위가 낮습니다. 다른 타입과 동일한 식별자를 사용하지 마세요.
return 2;
}
int operator ""_a(long double val) {
return 3;
}
EXPECT_TRUE(3.14_a == 3); // int operator ""_a(long double val) 버전이 호출됩니다.
(C++20~) 사용자 정의 리터럴 인자 규칙 char8_t
1
2
ReturnType operator ""_m(char8_t);
ReturnType operator ""_n(const char8_t*, size_t);
댓글남기기