2025. 5. 3. 18:34ㆍProgramming Language/C++
1. 사용자 정의 리터럴
사용자 정의 리터럴 (User-defined literal)은 C++11부터 도입된 기능으로 리터럴 값에 접미사를 붙여 그 값을 특정 타입이나 형식으로 자동 변환하는 문법이다.
2. 사용자 정의 리터럴의 사용법
사용자 정의 리터럴을 정의 할때에는 무조건 _언더바를 붙인 형태로 정의되게 되어 있다.
만약에 _언더바가 붙지 않은 리터럴의 경우는 표준 라이브러리에서 제공하는 리터럴로 인식하면 된다.
표준 라이브러리에서 제공하는 리터럴을 한번 보자면
chrono
chrono는 chrono라는 라이브러리에서 제공되는 리터럴이다.
그리고 chrono 리터럴을 사용하려면 std를 using namespace로 등록하거나 std::chrono_literals을 using namespace로 등록해야된다.
그리고 std::chrono::minute 타입의 변수에 값을 넣어줘야 한다.
그리고 이걸 cout으로 .count()라는 함수를 호출해주면
이는 24시간 5분을 분으로 환산했을때 1445분이라 그 1445가 출력된 것이다.
정의를 피킹해보면
이렇게 ""h로 연산자 오버로딩을 하는것을 볼 수 있다.
이런 것을 우리가 이제 만들어보도록 해보자.
우리는 길이에 대한 사용자 정의 리터럴을 만들어기위해서 먼저 Length라는 클래스를 하나 만들어주자.
그리고 내부에 const long double 타입 멤버 변수를 하나 선언해주고 생성사에서만 초기화해주고 그 후로는 변경되지 않는 이뮤터블 클래스(한 번 생성되면 내부 상태를 변경할 수 없는 클래스를 말함)를 만들어주자.
1) m 사용자 정의 리터럴
이제 전역 함수로 사용자 정의 리터럴을 만들건데 반환값은 Length타입이고 연산자는 "" _m으로 미터값에 대한 리터럴을 만들어주도록 하자.
그리고 우선 매개변수로 int형 값을 받아보면
이렇게 받을 수 없고 문자형식 혹은 unsigned long long 타입이여야 한다고 되어 있다.
사실 리터럴에 넘어갈 수 있는 자료형은 몆몆개로 정해져 있다.
보다싶이 실수일때는 어떤 타입이여야하고 정수일때는 어떤 타입이여야하고 뭐 문자타입일때는 뭐여야하고 문자열일땐 뭐여야하는지가 딱 정해져 있어서 이것에 맞게 매개변수를 받을수 밖에 없다.
그래서 우리는 정수를 받을 것이니까 unsigned long long int로 매개변수를 넣어주고
return 값은 Length 객체에 value를 넣어 반환해주자.
이러면 연산자 오버로딩이 완료 된것으로 이제 메인 함수에서 사용해보면
이렇게 안되는데 이건 msvc에서만 안되는 것으로 C++에서는 사용자 정의 리터럴 연산자를 클래스 내부에 friend로 정의 할 수있는데 MSVC는 이 문법을 제대로 지원하지 않는다.
그렇기에 외부에 전역 선언으로 변경해주도록 하자.
이렇게 변경해주면
정상적으로 되는 것을 볼 수 있다.
이렇게 정수에 _m을 붙여서 사용이 가능한것을 볼 수 있는데
이렇게 실수형은 안된다.
이제 실수형에 대한 오버로딩도 해보자.
이러면 정상적으로 되는 것을 볼 수 있다.
이렇게 만든 리터럴에 -연산자를 붙여보면
둘다 안된다고 나오는 것을 볼 수 있다.
이 리터럴에서는 음수란게 존재하지 않는다.
그렇기 때문에 사실상 - 연산이 존재하는 것으로 - 연산에 대한 연산자 오버로딩이 따로 필요하다.
반환 타입은 Length는 어차피 지금 변경될수 없는 값이기에 이거 자체를 반환하는 것은 당연히 말이 안되기에 참조 타입이 아닌 그냥 단순한 값의 복사를 해야만 한다.
또한 단항 연산자이기 때문에 따로 매개변수를 받지 않고 값 자체는 어차피 변경하지 못하는 것이기에 const 함수로 선언하고 return 값으로 Length객체를 하나 생성해서 -멤버 변수 로 던져주자.
이러면 문제 없이 해결되었다.
2) km 사용자 정의 리터럴
이제 km 사용자 정의 리터럴을 하나 만들어보자
km의 경우는 이전 m 사용자 정의 리터럴 만든것을 복사해주고
연산자를 _km로 변경해주고
value에 *1000을 해주자.
그리고 friend로 프로토 타입을 클래스 내부에 등록해주면
_km도 정상적으로 사용이 가능해진다.
만약 클래스 내부에
이렇게 생성자를 private로 만든다면
이렇게 객체를 생성할때에 생성자에 접근할 수 없게 되기에 객체를 생성할 수 없게 된다.
이 경우에는 나는 이 Length라는 클래스는 이렇게 생성자를 통해서 객체로 생성되게 안할거야
그냥 나는 이거 사용자 정의 리터럴을 통해서만 객체를 만들 수 있도록 클래스 만들어서 쓸거야 라고 하는 것이라고 보면 된다.
3) 리터럴 연산자 오버로딩
이제 km와 m을 더해서 연산이 되도록 연산자 오버로딩을 한번 해보자.
이 연산이 가능하게 연산자 오버로딩을 한번 구현해보자.
우선 이 반환값은 당연히 Length 타입일 것이고, + 연산에 우항에 해당하는 타입 또한 Length 타입이기에 매개변수도 Length타입으로 const Length& 참조 타입으로 받아주고 const 함수로 선언해주자.
그리고 내부에선 현재 부른 객체의 맴버변수 + 전달받은 매개변수의 멤버 변수를 합해 Length객체를 만들어 반환해주자.
그럼 정상적으로 동작하는 것을 볼 수 있다.
이걸 Length 타입에 넣어 확인해보면
print 문을 public으로 하나만들어주고
이렇게 해서 프린트 해보면
이렇게 m로 환산된 10.1km가 반환된다.
그리고 이걸 각각의 단위에 맞게 출력을 하고자 한다면 클래스 내부에 public으로
이렇게 선언해준 후에 main에서 함수를 호출해주면
이렇게 호출도 된다.
여기에 추가로 센티미터, 밀리미터 그리고 마이너스 연산까지 추가해보자.
3-1) 센티미터
센티미터의 경우 100센치가 1미터이기에 멤버 변수에 / 100을 해주면 센치미터가 된다.
유의해야할 점은 정수가 전달되는 경우는 정수 / 정수의 몫이 0일 경우, 그니까 100보다 작은 경우는 몫이 0이 나오게 된다.
또한 정확하게 떨어지는 값이 나오지도 않으니 정수 끼리의 나눗셈을 할때는 실수로 변경한 후에 계산을 해야한다.
그래서 위처럼 해야하는게 아니라 정수로 전달된 value는 실수로 변환하여야 하고 실수로 변환하는 방법은
이렇게 계산해야한다.
* 생각해보니 0.01을 곱해주면 됐었음...;;
3-2) 밀리미터
밀리미터는 10밀리 미터가 1센치기에 1센치와 10배 1미터와는 1000배 차이가 나기에 / 1000을 해주면 밀리미터 단위가 된다.
cm와 동일하게 유의해야할 점은 정수가 전달되는 경우는 정수 / 정수의 몫이 0일 경우, 그니까 1000보다 작은 경우는 몫이 0이 나오게 된다.
또한 정확하게 떨어지는 값이 나오지도 않으니 정수 끼리의 나눗셈을 할때는 실수로 변경한 후에 계산을 해야한다.
그래서 사실은
이렇게 해야 정확한 계산이 된다.
* 생각해보니 0.001을 곱해주면 됐었음...;;
3-3) - 연산 구현
- 연산도 +와 다를 점은 없다 반환값은 Length 타입으로 연산자만 -로 변경하고 매개변수 또한 const Length& 참조 타입으로 const 함수를 생성해주고 내부에서 return 으로 Length 타입에 멤버변수 - 전달받은 매개변수의 멤버변수로 객체를 생성해서 던져주자.
출력해보면
이렇게 잘 나오게 된다.
이런 데이터를 사용하는 사람들은 단위를 통일하기에 매우 편리한 방법이라고 한다.
'Programming Language > C++' 카테고리의 다른 글
Part2::Ch 03. 상속 - 02. 가상 함수 (0) | 2025.05.04 |
---|---|
Part2::Ch 03. 상속 - 01. 상속의 기본 (0) | 2025.05.04 |
Part2::Ch 02. 연산자 오버로딩- 09. 호출 연산자 오버로딩, 함수 객체 (0) | 2025.05.03 |
Part2::Ch 02. 연산자 오버로딩- 08. 변환 연산자 오버로딩, 변환 생성자, explicit (0) | 2025.05.03 |
Part2::Ch 02. 연산자 오버로딩- 07. 대입 연산자 오버로딩, 복사 생성자 (0) | 2025.05.03 |