Software Workshop

C++11부터 사용자 정의 리터럴(user defined literal)을 만들 수 있다. 그러나 실제로 표준 라이브러리에서 사용자 정의 리터럴을 제공하고 있지 않다가 C++14부터 표준 라이브러리에 사용자 정의 리터럴이 도입되기 시작했다.

예를 들어, 아래 예제의 1.2_w나 u"one"_w와 같은 형태로 사용자가 직접 정의한 리터럴을 사용할 수 있다.
사용자 정의 리터럴은 C++ 언어 차원에서 기본적으로 제공하는 리터럴에 suffix를 붙여서 정의할 수 있다.

애플리케이션 코드에서는 _로 시작하는 suffix를 사용하면 된다.

long double operator "" _w(long double);
std::string operator "" _w(const char16_t*, size_t);
unsigned operator "" _w(const char*);

int main(void) {
    1.2_w; // calls operator "" _w(1.2L)
    u"one"_w; // calls operator "" _w(u"one", 3)
    12_w; // calls operator "" _w("12")
    "two"_w; // error: no applicable literal operator
}

이 코드는 개별 operator들이 선언만 되어 있고 정의되어 있지 않아서 실행가능한 바이너리를 빌드하지 못한다.

실제로 동작하는 가장 간단한 예제는 다음과 같다.

#include <iostream>
#include <string>

using namespace std;

int main(void)
{
    string str = "hello"s + "world"s;
    cout << str << endl;

    return 0;
}

C++14 표준 라이브러리에 s라는 suffix로 문자열 리터럴을 정의해두었기 때문에 "hello"s 형태로 사용 가능하다.

helloworld

같은 suffix가 다른 타입의 사용자 정의 리터럴로 사용될 수 있다.

#include <iostream>
#include <chrono>

using namespace std;

int main(void)
{
    auto threeSeconds = 3s;

    return 0;
}

"hello"s는 string 타입의 리터럴이 되고, 3s는 chrono 타입(여기서는 초 단위)의 리터럴이 된다.

사용자 정의 리터럴의 아주 간단한 예제를 만들어보겠다.
킬로미터와 마일 단위로 표기된 사용자 정의 리터럴을 정의한다.
아래 예제에서 36.7_km과 43.5_mi이 각각 킬로미터와 마일 단위 리터럴에 해당한다.

#include <iostream>
#include <string>
#include <sstream>

using namespace std;

const long double km_per_mile = 1.609344L;
long double operator""_km(const long double val) {
    return val;
}
long double operator""_mi(const long double val) {
    return val * km_per_mile;
}

int main(void)
{
    cout << 36.7_km << endl;
    cout << 43.5_mi << endl;

    return 0;
}

마일 단위 리터럴은 킬로미터로 환산해서 값을 반환하도록 operator를 정의하였다.

36.7
70.0065

이 예제를 확장해서 Distance라는 클래스를 만들어보겠다.
이 클래스는 내부적으로 kilometers라는 변수에 값을 저장하고 사용자 정의 리터럴 연산자를 friend 함수로 정의해서 사용하고 있다.
클래스의 + 연산자도 오버로딩하여 정의하였다.
클래스의 << 연산자도 오버로딩하여 킬로미터 단위로 값을 출력하고 "km"라는 문자열을 덧붙여 출력하도록 하였다.

#include <iostream>
#include <string>

using namespace std;

class 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:
    constexpr static long double km_per_mile = 1.609344L;
    long double get_kilometers() const {
        return kilometers;
    }
    Distance operator+(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 * Distance::km_per_mile);
}
ostream& operator<<(ostream& os, const Distance& d) {
    os << d.get_kilometers() << "km";
    return os;
}

int main(void)
{
    Distance d = 36.0_mi + 42.0_km;
    cout << d << endl;

    return 0;
}
99.9364km