본문으로 바로가기

C++11) 사용자 정의 리터럴

category C++/Modern 2019. 2. 13. 03:40

<개요>


리터럴의 5가지 주요 자료형은 정수, 문자, 부동 소수점, 문자열, 포인터 및 부울형 입니다.

C++11 부터 이러한 범주에 따라 사용자 고유의 리터럴을 정의하여 일반적인 구문에 대한 바로가기를 정의하고 안전성을 높힐 수 있습니다. 


 예를 들어 Distance 라는 클래스가 있다고 합시다. 킬로미터와 마일에 대한 리터럴을 하나씩 정의하고 간단히

auto = 42.0km 또는 auto = 42.0mi을 작성하여 사용자가 측정 단위를 명시할 수 있습니다.

사용자 정의 리터럴에 대한 성능의 이점이나 단점은 없습니다.

주로 편의상 또는 컴파일 시간 형식 추론을 위해 사용됩니다.


1
2
3
4
5
Distance d = 36.0_mi + 42.0_km;         // 사용자 정의 리터럴
    std::string str = "hello"+ "World"s;  // 표준 라이브러리인 <string> 의 리터럴
    complex<double> num =
        (2.0 + 3.01i) * (5.0 + 4.3i);       // 표준 라이브러리인 <complex> 의 리터럴
    auto duration = 15ms + 42h;             // 표준 라이브러리인 <chrono> 의 리터럴
cs

사용자 정의 리터럴을 구현하시려면 ""연산자와 범주에 속하는 인자를 가지고 해야합니다.

1
2
3
4
5
6
7
8
9
10
11
12
ReturnType operator "" _a(unsigned long long int);   // int 형 사용자 리터럴
ReturnType operator "" _b(long double);              // double 형 사용자 리터럴
ReturnType operator "" _c(char);                     // char 형 사용자 리터럴
ReturnType operator "" _d(wchar_t);                  // char 형 사용자 리터럴
ReturnType operator "" _e(char16_t);                 // char 형 사용자 리터럴
ReturnType operator "" _f(char32_t);                 // char 형 사용자 리터럴
ReturnType operator "" _g(const     char*, size_t);  // 문자열(char* 또는 string) 형 사용자 리터럴
ReturnType operator "" _h(const  wchar_t*, size_t);  // 문자열(char* 또는 string) 형 사용자 리터럴
ReturnType operator "" _i(const char16_t*, size_t);  // 문자열(char* 또는 string) 형 사용자 리터럴
ReturnType operator "" _g(const char32_t*, size_t);  // 문자열(char* 또는 string) 형 사용자 리터럴
ReturnType operator "" _r(const char*);              // 원시 리터럴 연산자
template<char...> ReturnType operator "" _t();       // 템플릿 리터럴 연산자
cs

연산자 이름은 제공하는 이름에 대한 순서이지만 (_a, _b, _c 같은) 선행밑줄은 꼭 필요합니다 ( _a 에서 _는 꼭 써야합니다 ! )
_가 없는 리터럴은 표준 라이브러리 리터럴 뿐입니다.
연산자 내에서 추가작업을 할 수도 있고, constexpr 로 선언할 수도 있습니다.

소스 코드에서 사용자 정의 여부에 관계없이 모든 리터럴은 본질적으로 101, 54.7, "hello" 또는 true와 같은 영숫자 문자의 시퀀스입니다. 
컴파일러가 리터럴 값으로 할당 한 모든 형식을 입력으로 허용 하는 사용자 정의 리터럴을 비공식적으로 가공된 리터럴이라고 합니다.
_r 및 _t를 제외한 위의 모든 연산자는 가공된 리터럴입니다. 
예를 들어 리터럴 42.0_km는 _b와 유사한 서명을 가진 _km이라는 연산자에 바인딩하고 리터럴 42_km은 _a와 유사한 서명을 가진 연산자에 바인딩합니다.

다음 예제에서는 사용자 정의 리터럴을 통해 호출자에게 명시적으로 입력을 지정하도록 장려하는 방법을 보여 줍니다.
Distance를 생성하려면 사용자가 적절한 사용자 정의 리터럴을 사용하여 킬로미터 또는 마일을 명시적으로 지정해야 합니다. 
물론 다른 방법으로 동일한 결과를 얻을 수도 있지만 사용자 정의 리터럴이 대체 방법보다 더 간단합니다.

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
struct Distance
{
private:
    explicit Distance(long double val) : kilometers(val)
    {}
 
    friend Distance operator"" _km(long double  val);
    friend Distance operator"" _mi(long double val);
    long double kilometers{ 0 };
public:
    long double get_kilometers() { return kilometers; }
    Distance operator+(const Distance& other)
    {
        return Distance(get_kilometers() + other.get_kilometers());
    }
};
 
Distance operator"" _km(long double  val)
{
    return Distance(val);
}
 
Distance operator"" _mi(long double val)
{
    return Distance(val * 1.6);
}
int main(int argc, char* argv[])
{
    // 오퍼레이터에 사용되려면 . 을 꼭 사용하셔야 합니다 (double 형이므로)
    Distance d{ 402.0_km }; // kilometer 와 매칭됩니다
    cout << "Kilometers in d: " << d.get_kilometers() << endl// 402
 
    Distance d2{ 402.0_mi }; // mile 과 매칭됩니다.
    cout << "Kilometers in d2: " << d2.get_kilometers() << endl;  //643.2
 
    // 다른 타입에 대해서 +연산을 할 수도 있습니다.
    Distance d3 = 36.0_mi + 42.0_km;
    cout << "d3 value = " << d3.get_kilometers() << endl// 99.6
 
     Distance d4(90.0); // error, 맞는 사용자 리터럴이 없습니다 ( 기본 생성자는 접근 불가 )
}
cs

리터럴 숫자는 10진수를 사용해야 합니다.
그렇지 않으면 숫자가 정수로 해석되며 형식이 연산자와 호환되지 않습니다. 
부동소수점, 정수형은 long long ~ 여야 합니다!

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
27
28
29
30
31
32
33
#include <iostream>
using namespace std;
 
class MyComplex {
public:
    long double getS() { return s; }
    long double getH() { return h; }
private:
    friend MyComplex operator"" _i(long double val);
    friend MyComplex operator+(long double lhs, const MyComplex& rhs);
    MyComplex operator+(const MyComplex& rhs) const {
        return MyComplex(s + rhs.s, h + rhs.h);
    }
    explicit MyComplex(long double _s, long double _h) :
    s(_s), h(_h) { }
    explicit MyComplex(long double _s) : s(_s) { }
 
private:
    long double s;
    long double h;
};
MyComplex operator+(long double lhs, const MyComplex& rhs) {
    return MyComplex(lhs, rhs.h);
}
MyComplex operator"" _i(long double val) {
    return MyComplex(0, val);
}
int main() {
    MyComplex a = { 10.0 + 10.0_i };
 
    cout << a.getS() << " + " << a.getH() << 'i' << endl;
    // 10 + 10i 가 출력 됩니다!
}
cs


내가 만든 간단한 복소수의 예


링크 : https://docs.microsoft.com/ko-kr/cpp/cpp/user-defined-literals-cpp?view=vs-2017