본문 바로가기

3.구현/C or C++

[c++] nullptr, using, enum class 사용하기

들어가기

이번에는 C++11에서 nullptr, using, enum class에 대해서 살펴보자.

작성자: ospace114@empal.com, http://ospace.tistory.com/

nullptr 사용

기존 NULL 또는 0 대신에 사용할 수 있는 nullptr가 있다. NULL도 내부적으로 0으로 정의되어 있는 경우가 대부분이다. 이는 0값과 혼동이 발생할 수 있기 때문에 nullptr을 사용을 권장하다. 이는 템플릿 사용할 때에 형식 연역시 유용하다. 기존 NULL은 int 형식으로 인지하면서 에러가 발생할 수 있다.

template<typename FuncT, typename PtrT>
auto foo(FuncT& func, PtrT ptr) {
    return func(ptr);
}

int f(std::shared_ptr<Box> spb);

foo(f, NULL); // 에러
foo(f, nullptr);

nullptr인 객체는 조건 식에서는 false으로 처리된다.

int *val = nullptr;
if (val) {
    std::cout << *val << std::endl;
} else {
    std::cout << "val is null." << std::endl;
}

using 사용

typedef에 의해서 타입을 정의해서 사용했다. 템플릿 클래스의 긴 표현식을 별칭으로 짧게 줄여쓰는데 유용했다. 이를 using이라는 별칭선언(alias declaration)으로 대처할 수 있고 활용도도 더 좋다.

typedef std::unique_ptr<std::vector<std::string>> PtrStrings;
using PtrStrings = std::unique_ptr<std::vector<std::string>>;

typedef void (*FP)(void);
using FP = void (*)(void);

그리고 using에서 템플릿을 사용해서 별칭 선언도 할 수 있다. 이를 이용해 C++14에서 remove_const_t가 아래처럼 선언되어 있다.

template<typename T>
using remove_const_t<T> = typename remove_const<T>::type

enum 사용

C++98가지는 범위없는(unscoped) enum으로 열거자들이 범위 없이 그대로 사용된다. 열거자들은 암묵적으로 0에서 0xFFFFFFFF까지 값을 할당할 수 있다. 작은 값은 char형 큰 값은 int형으로 선택된다. 초기값이 미정될 경우 0부터 차례로 값이 할당된다.

#include <iostream>

enum Color { red, green, blue };

int main() {
    std::cout << " green is " << green << ".\n";
}

C++11부터는 enum class 구문으로 범위있는(scoped) enum을 사용한다. enum class 사용으로 같은 이름을 사용할 위험이 적다. 그리고 정수형 데이터가 아니기에 잘못된 값 비교로 인한 잠재적 위험을 줄인다. 물론 static_cast을 이용해서 이전처럼 정수형, 혹은 다른 형으로 변환해서 사용할 수 있다.

#include <iostream>

enum class Color { red, green, blue };

int main() {
    Color green = Color::green;
    int greenVal = static_cast<int>(green);
    std::cout << " green is " << greenVal << ".\n";
}

enum이 기본 형식이 char 혹은 int로 고정되어 있지만 enum class는 기본 int을 사용하거나 변경할 수도 있다.

enum class Color; // 기본 형식 int
enum class Color : std::uint32_t; // 기본 형식 uint32_t

enum class을 사용할 때 장점으로 전방선언을 할 수 있다. 기존 enum은 사용할 경우 반드시 include해서 사용해야 한다. 그래서 보통 헤더에 포함되는데 enum이 수정될 때마다 관련 모든 파일을 재 컴파일해야 한다. 정방선언을 사용할 경우 헤더는 변경되지 않고 별도 소스에서 enum class을 정의하기 때문에 재 컴파일해야하는 경우를 줄일 수 있다.

enum class Color;
void displayColor(Color c);

기존 enum과 tuple을 활용팁이다.

// enum과 tuple에 필드를 순서를 유지하여 서로 연관시킴
enum UserFields { uiName, uiEmail, uiReputation };
using UserInfo = std::tuple<std::string, std::string, std::size_t>;

UserInfo user;
auto email = std::get<uiEmail>(user);

enum class을 사용할 경우 코드가 장황해진다. 물론 범위로 인한 이름 오염은 줄어든다.


// enum class를 바탕형식으로 변환되고 std::get을 위해 noexcept로 정의
template<typename E>
constexpr auto toUType(E enumerator) noexcept {
    return static_cast<typename std::underlying_type<E>::type>(enumerator);
}

enum class UserFileds { uiName, uiEmail, uiReputation };
using UserInfo = std::tuple<std::string, std::string, std::size_t>;

UserInfo user;
auto email = std::get<toUType(UserFields::uiEmail)>(user);

C++14는 std::unerlying_type_t로 좀던 간편하게 사용할 수 있다.

마무리

이전에도 NULL이 없을 때 0을 NULL로 정의해서 사용했었다. nullptr을 사용하면서 좀더 명확히 처리할 수 있게 되었네요. 또한 using으로 더 쉽고 간단하게 타입을 정의해서 사용할 수 있습니다.
최대한 쉽고 간략하게 정리했는데, 여러분에 도움이 되었으면 합니다. 즐프하세요. ospace.

참고

[1] 스콧 마이어스 저, 류광 역, Effective Modern C++, 인사이트

반응형