User-defined literals (since C++11)
사용자 정의 접미사를 정의하여 정수, 부동 소수점, 문자 및 문자열 리터럴이 사용자 정의 타입의 객체를 생성할 수 있도록 합니다.
목차 |
구문
사용자 정의 리터럴은 다음 형태 중 하나의 표현식입니다
| decimal-literal ud-suffix | (1) | ||||||||
| octal-literal ud-suffix | (2) | ||||||||
| hex-literal ud-suffix | (3) | ||||||||
| binary-literal ud-suffix | (4) | ||||||||
| fractional-constant exponent-part (optional) ud-suffix | (5) | ||||||||
| digit-sequence exponent-part ud-suffix | (6) | ||||||||
| character-literal ud-suffix | (7) | ||||||||
| string-literal ud-suffix | (8) | ||||||||
태그 내 내용, C++ 관련 용어는 번역하지 않고 원본을 유지했습니다. 번역이 필요한 일반 텍스트가 없으므로 원본 구조를 그대로 유지합니다.
| decimal-literal | - | 정수 리터럴 에서와 동일, 0이 아닌 십진수 숫자 하나와 그 뒤에 오는 0개 이상의 십진수 숫자들 |
| octal-literal | - | 정수 리터럴 에서와 동일, 0 하나와 그 뒤에 오는 0개 이상의 팔진수 숫자들 |
| hex-literal | - |
정수 리터럴
에서와 동일,
0x
또는
0X
뒤에 하나 이상의 십육진수 숫자들
|
| binary-literal | - |
정수 리터럴
에서와 동일,
0b
또는
0B
뒤에 하나 이상의 이진수 숫자들
|
| digit-sequence | - | 부동 소수점 리터럴 에서와 동일, 십진수 숫자들의 연속 |
| fractional-constant | - | 부동 소수점 리터럴 에서와 동일, digit-sequence 뒤에 점( 123 . )이 오거나, 선택적 digit-sequence 뒤에 점과 또 다른 digit-sequence 가 옴 ( 1.0 또는 .12 ) |
| exponent-part | - |
부동 소수점 리터럴
에서와 동일, 문자
e
또는 문자
E
뒤에 선택적 부호, 그리고
digit-sequence
가 옴
|
| character-literal | - | 문자 리터럴 에서와 동일 |
| string-literal | - | 문자열 리터럴 에서와 동일, 원시 문자열 리터럴 포함 |
| ud-suffix | - | 식별자, 리터럴 연산자 또는 리터럴 연산자 템플릿 선언에 의해 도입됨 ( 아래 참조) |
| (C++14부터) |
토큰이 사용자 정의 리터럴 구문과 일반 리터럴 구문 모두와 일치하는 경우, 해당 토큰은 일반 리터럴로 간주됩니다(즉,
LL
을
123LL
에서 오버로드하는 것은 불가능합니다).
컴파일러가 사용자 정의 리터럴을
ud-suffix
X
와 함께 만나면,
비정규 이름 조회
를 수행하여
operator
""
X
라는 이름의 함수를 찾습니다. 조회가 선언을 찾지 못하면 프로그램은 잘못된 형식입니다. 그렇지 않은 경우,
|
a)
오버로드 집합에
str
이 올바른 템플릿 인자로 사용되는 상수 템플릿 매개변수를 가진 문자열 리터럴 연산자 템플릿이 포함된 경우, 사용자 정의 리터럴 표현식은 함수 호출
operator
""
X
<
str
>
(
)
로 처리됩니다;
|
(since C++20) |
long double operator ""_w(long double); std::string operator ""_w(const char16_t*, size_t); unsigned operator ""_w(const char*); int main() { 1.2_w; // operator ""_w(1.2L) 호출 u"one"_w; // operator ""_w(u"one", 3) 호출 12_w; // operator ""_w("12") 호출 "two"_w; // 오류: 적용 가능한 리터럴 연산자가 없음 }
문자열 리터럴 연결이 번역 단계 6 에서 발생할 때, 사용자 정의 문자열 리터럴들도 함께 연결되며, 연결된 리터럴들 전체에 단 하나의 접미사만 나타날 수 있다는 점을 제외하고는 연결 목적상 그들의 ud-suffix 들은 무시됩니다:
int main() { L"A" "B" "C"_x; // OK: L"ABC"_x와 동일함 "P"_x "Q" "R"_y; // 오류: 두 개의 다른 사용자 정의 접미사(_x와 _y) }
리터럴 연산자
사용자 정의 리터럴에 의해 호출되는 함수는 literal operator (또는 템플릿인 경우 literal operator template )로 알려져 있습니다. 이는 네임스페이스 범위에서 다른 function 또는 function template 와 마찬가지로 선언됩니다(이는 friend function, 함수 템플릿의 명시적 인스턴스화 또는 특수화, 혹은 using-declaration에 의해 도입될 수도 있습니다). 단, 다음과 같은 제한 사항이 적용됩니다:
이 함수의 이름은 두 가지 형태 중 하나를 가질 수 있습니다:
operator ""
identifier
|
(1) | (사용 중단됨) | |||||||
operator
user-defined-string-literal
|
(2) | ||||||||
| identifier | - | 이 함수를 호출할 사용자 정의 리터럴의 ud-suffix 로 사용할 identifier |
| user-defined-string-literal | - |
""
문자 시퀀스 뒤에 공백 없이 이어지는,
ud-suffix
가 되는 문자 시퀀스
|
ud-suffix
는 밑줄
_
로 시작해야 합니다: 밑줄로 시작하지 않는 접미사는 표준 라이브러리에서 제공하는 리터럴 연산자를 위해 예약되어 있습니다. 또한 이중 밑줄
__
을 포함할 수 없습니다: 이러한 접미사 역시 예약되어 있습니다.
리터럴 연산자가 템플릿인 경우, 빈 매개변수 목록을 가져야 하며 하나의 템플릿 매개변수만 가질 수 있습니다. 이 매개변수는 요소 타입이 char 인 상수 템플릿 매개변수 팩이어야 합니다 (이 경우 numeric literal operator template 으로 알려져 있습니다):
template<char...> double operator ""_x();
|
또는 클래스 타입의 상수 템플릿 매개변수인 경우(이 경우 문자열 리터럴 연산자 템플릿 으로 알려져 있음): struct A { constexpr A(const char*); }; template<A a> A operator ""_a(); |
(since C++20) |
리터럴 연산자에는 다음 매개변수 목록만 허용됩니다:
(
const
char
*
)
|
(1) | ||||||||
(
unsigned
long
long
int
)
|
(2) | ||||||||
(
long
double
)
|
(3) | ||||||||
(
char
)
|
(4) | ||||||||
(
wchar_t
)
|
(5) | ||||||||
(
char8_t
)
|
(6) | (C++20부터) | |||||||
(
char16_t
)
|
(7) | ||||||||
(
char32_t
)
|
(8) | ||||||||
(
const
char
*
,
std::size_t
)
|
(9) | ||||||||
(
const
wchar_t
*
,
std::size_t
)
|
(10) | ||||||||
(
const
char8_t
*
,
std::size_t
)
|
(11) | (C++20부터) | |||||||
(
const
char16_t
*
,
std::size_t
)
|
(12) | ||||||||
(
const
char32_t
*
,
std::size_t
)
|
(13) | ||||||||
기본 인수 는 허용되지 않습니다.
C language linkage 는 허용되지 않습니다.
위의 제한 사항을 제외하면, 리터럴 연산자와 리터럴 연산자 템플릿은 일반 함수(및 함수 템플릿)이며, inline이나 constexpr로 선언될 수 있고, 내부 또는 외부 링크를 가질 수 있으며, 명시적으로 호출될 수 있고, 주소를 취할 수 있습니다 등.
#include <string> void operator ""_km(long double); // OK, 1.0_km에 대해 호출됨 void operator "" _km(long double); // 위와 동일, 사용 권장되지 않음 std::string operator ""_i18n(const char*, std::size_t); // OK template<char...> double operator ""_pi(); // OK float operator ""_e(const char*); // OK // 오류: 접미사는 밑줄로 시작해야 함 float operator ""Z(const char*); // 오류: 밑줄로 시작하고 대문자가 뒤따르는 모든 이름은 예약됨 // (참고: ""와 _ 사이에 공백이 있음) double operator"" _Z(long double); // OK. 참고: ""와 _ 사이에 공백 없음 double operator""_Z(long double); // OK: 리터럴 연산자는 오버로드 가능 double operator ""_Z(const char* args); int main() {}
참고 사항
사용자 정의 리터럴이 도입된 이후, 앞선 문자열 리터럴 뒤에 공백 없이 고정 너비 정수 타입에 대한 형식 매크로 상수 를 사용하는 코드는 유효하지 않게 되었습니다: std:: printf ( "%" PRId64 " \n " , INT64_MIN ) ; 는 다음으로 대체되어야 합니다: std:: printf ( "%" PRId64 " \n " , INT64_MIN ) ; .
최대한 많이 인식(maximal munch)
규칙으로 인해,
p
,
P
,
(C++17부터)
e
및
E
로 끝나는 사용자 정의 정수 및 부동 소수점 리터럴은
+
또는
-
연산자가 뒤따를 경우, 소스 코드에서 공백이나 괄호로 연산자와 분리해야 합니다:
long double operator""_E(long double); long double operator""_a(long double); int operator""_p(unsigned long long); auto x = 1.0_E+2.0; // 오류 auto y = 1.0_a+2.0; // 정상 auto z = 1.0_E +2.0; // 정상 auto q = (1.0_E)+2.0; // 정상 auto w = 1_p+2; // 오류 auto u = 1_p +2; // 정상
정수나 부동소수점 사용자 정의 리터럴 뒤에 오는 점 연산자에도 동일하게 적용됩니다:
#include <chrono> using namespace std::literals; auto a = 4s.count(); // 오류 auto b = 4s .count(); // 정상 auto c = (4s).count(); // 정상
그렇지 않으면 단일 유효하지 않은 전처리 숫자 토큰(예: 1.0 _E + 2.0 또는 4s. count )이 생성되어 컴파일이 실패하게 됩니다.
| 기능 테스트 매크로 | 값 | 표준 | 기능 |
|---|---|---|---|
__cpp_user_defined_literals
|
200809L
|
(C++11) | 사용자 정의 리터럴 |
키워드
예제
#include <algorithm> #include <cstddef> #include <iostream> #include <numbers> #include <string> // 입력 파라미터인 도(degrees)를 라디안(radians)으로 변환하는 용도 constexpr long double operator""_deg_to_rad(long double deg) { long double radians = deg * std::numbers::pi_v<long double> / 180; return radians; } // 사용자 정의 타입과 함께 사용 struct mytype { unsigned long long m; }; constexpr mytype operator""_mytype(unsigned long long n) { return mytype{n}; } // 부수 효과(side-effects)를 위한 용도 void operator""_print(const char* str) { std::cout << str << '\n'; } #if __cpp_nontype_template_args < 201911 std::string operator""_x2 (const char* str, std::size_t) { return std::string{str} + str; } #else // C++20 문자열 리터럴 연산자 템플릿 template<std::size_t N> struct DoubleString { char p[N + N - 1]{}; constexpr DoubleString(char const(&pp)[N]) { std::ranges::copy(pp, p); std::ranges::copy(pp, p + N - 1); } }; template<DoubleString A> constexpr auto operator""_x2() { return A.p; } #endif // C++20 int main() { double x_rad = 90.0_deg_to_rad; std::cout << std::fixed << x_rad << '\n'; mytype y = 123_mytype; std::cout << y.m << '\n'; 0x123ABC_print; std::cout << "abc"_x2 << '\n'; }
출력:
1.570796 123 0x123ABC abcabc
표준 라이브러리
다음 리터럴 연산자들은 표준 라이브러리에 정의되어 있습니다:
|
인라인 네임스페이스에 정의됨
std::literals::complex_literals
|
|
|
순수 허수를 나타내는
std::complex
리터럴
(함수) |
|
|
인라인 네임스페이스에 정의됨
std::literals::chrono_literals
|
|
|
(C++14)
|
시간을 나타내는
std::chrono::duration
리터럴
(함수) |
|
(C++14)
|
분을 나타내는
std::chrono::duration
리터럴
(함수) |
|
(C++14)
|
초를 나타내는
std::chrono::duration
리터럴
(함수) |
|
(C++14)
|
밀리초를 나타내는
std::chrono::duration
리터럴
(함수) |
|
(C++14)
|
마이크로초를 나타내는
std::chrono::duration
리터럴
(함수) |
|
(C++14)
|
나노초를 나타내는
std::chrono::duration
리터럴
(함수) |
|
(C++20)
|
특정 연도를 나타내는
std::chrono::year
리터럴
(함수) |
|
(C++20)
|
월의 특정 날짜를 나타내는
std::chrono::day
리터럴
(함수) |
|
인라인 네임스페이스에 정의됨
std::literals::string_literals
|
|
|
(C++14)
|
문자 배열 리터럴을
basic_string
으로 변환
(함수) |
|
인라인 네임스페이스에 정의됨
std::literals::string_view_literals
|
|
|
(C++17)
|
문자 배열 리터럴의 string view를 생성
(함수) |
결함 보고서
다음의 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.
| DR | 적용 대상 | 게시된 동작 | 올바른 동작 |
|---|---|---|---|
| CWG 1473 | C++11 |
리터럴 연산자 선언에서
""
와
ud-suffix
사이의 공백이
필요했음 |
선택적으로 변경됨 |
| CWG 1479 | C++11 | 리터럴 연산자가 기본 인수를 가질 수 있었음 | 금지됨 |
| CWG 2521 | C++11 |
operator
""
_Bq
는 잘못된 형식이었음 (진단 불필요)
예약된 식별자
_Bq
를 사용하기 때문
|
""
와
ud-suffix
사이에 공백이 있는
리터럴 연산자 구문을 사용하지 않도록 변경됨 |