C++ 타입 선택 조언: struct·class·enum·variant 언제 쓰나 (Tour 2.6)

한 줄 답: C++에서 기본 타입만으로 의미 표현이 부족할 때, 관련 데이터를 묶고 잘못된 상태를 방지할 수 있도록 용도에 맞는 사용자 정의 타입(struct, class, enum, variant)을 설계하고 선택합니다.

이 글은 A Tour of C++ 2.6 Advice 학습노트를 기준으로 정리한 내용입니다. 2장(User-Defined Types)에서 배운 struct, class, enum, union, variant를 어떻게 사용하는 것이 좋은지 정리하는 절이며, 2.5 Unions의 union 메모리 구조를 다시 처음부터 설명하지는 않습니다.

struct·class·enum·variant는 언제 고르면 됩니까?

한 줄 답: 연관된 데이터 묶음은 struct, 구현을 숨기고 인터페이스만 노출할 때는 class, 상탯값 집합은 enum class, 여러 타입 중 하나의 값을 타입 안전하게 보관할 때는 std::variant를 선택합니다.

기본 타입인 int, double만으로 의미를 충분히 표현하기 어렵다면 의미가 드러나는 타입을 직접 만듭니다. 학습노트의 예제는 다음과 같습니다.

struct Celsius {
    double value;
};

단순한 double temperature보다 Celsius라는 타입이 값의 의미를 더 명확하게 표현합니다. 서로 관련된 값은 각각 따로 관리하기보다 하나의 개념으로 묶는 것이 좋습니다.

struct Point {
    double x;
    double y;
};

반면 외부에는 필요한 interface만 공개하고 내부 구현은 숨기고 싶다면 class를 사용합니다.

class Counter {
public:
    void increment();
private:
    int value = 0;
};

struct는 기본 접근 권한이 public인 class라고 볼 수 있습니다. 다음 예제에서 A::x는 public이고 B::x는 private입니다.

struct A {
    int x;   // public
};
class B {
    int x;   // private
};

이름이 있는 상수 집합을 다룰 때는 상태를 0, 1, 2 같은 숫자로 직접 표현하기보다 enum class로 의미 있는 이름을 사용합니다.

enum class State {
    idle,
    running,
    error
};

struct·class·enum·variant 선택 기준

struct·class·enum·variant 선택 기준

naked union 대신 std::variant를 선호하는 이유는 무엇입니까?

한 줄 답: 프로그래머가 직접 상태를 관리해야 하는 naked union과 달리, std::variant는 현재 어떤 타입이 활성화되어 있는지 스스로 추적하여 타입 안전성을 보장하기 때문입니다.

union만 직접 사용하면 어떤 멤버가 active member인지 프로그래머가 직접 추적해야 합니다.

union Value {
    int i;
    double d;
};

필요하다면 tag와 union을 class 안에 함께 넣어 상태를 관리할 수도 있지만, 가능하면 naked union보다 std::variant를 선호합니다.

std::variant value;

std::variant는 현재 어떤 타입이 들어 있는지 스스로 관리하므로 raw union보다 타입 안전하게 사용할 수 있습니다. 또한 상태값을 단순한 숫자처럼 취급하기보다, 해당 enum이나 variant의 의미에 맞는 함수·연산을 함께 정의해 안전하게 사용하는 것이 좋습니다.

naked union과 std::variant 비교

naked union과 std::variant 비교

2.6 Advice가 말하는 타입 설계의 핵심 철학은 무엇입니까?

한 줄 답: 데이터의 의미를 명확히 표현하는 타입을 정의하고, 생성자(Constructor) 등을 통해 객체가 생성되는 순간부터 유효한 상태를 보장하게 설계하는 것입니다.

class User {
public:
    User(int id) : id{id} {}
private:
    int id;
};

2장의 핵심 철학은 데이터의 의미를 타입으로 표현하고, 잘못된 상태를 만들기 어렵게 설계하라는 것입니다. 다음 두 코드를 비교하면 그 차이가 드러납니다.

// 나쁜 예
int status = 0;

// 좋은 예
enum class Status { idle, running, error };
Status status = Status::idle;

int status는 어떤 값이 유효한지, 각 숫자가 무엇을 뜻하는지 타입만으로는 알 수 없습니다. 반대로 enum class Status는 가능한 상태를 이름으로 제한하여, 정의되지 않은 값이 들어올 수 없게 만듭니다. 이처럼 struct, class, enum, variant를 목적에 맞게 골라 사용하는 것이 2.6 Advice가 전달하는 핵심입니다.

FAQ

Q. struct와 class의 차이점은 무엇입니까?

A. 기본 접근 권한(Access Level)이 다릅니다. struct는 기본값이 public이며, class는 기본값이 private입니다. 용도에 맞게 선택하여 사용합니다.

Q. 상태를 표현할 때 int 숫자 대신 enum class를 권장하는 이유는 무엇입니까?

A. enum class는 코드의 의미를 명확히 드러내고, 이름 충돌과 의도치 않은 암시적 정수 변환을 방지하여 더 안전한 코드를 작성할 수 있게 해주기 때문입니다.

출처

C++ 학습노트 — 2.6 Advice

cppreference: enum

cppreference: std::variant