본문으로 바로가기

C++11) constexpr 키워드와 강한 열거형

category C++/Modern 2019. 2. 5. 03:39

<개요>


constexpr 키워드는 상수 식을 뜻하며, c++11에서 추가됐고 c++14에서 향상됐습니다.

const 처럼 값을 변경하려고 할 시 컴파일러가 오류를 발생합니다. 

const 와는 달리, constexpr는 함수 및 클래스에서도 사용이 가능합니다.

constexpr 는 template 의 인수 또는 배열의 사이즈를 정의할때 마다 사용이 가능합니다.


강한 열거형 (strongly - typed enum class) 는 기존의 열거형에서 스코프 연산자만을 사용해서 타입에 접근 가능한 열거형입니다.


<constexpr>


constexpr는 c++11 에서 추가된 키워드로서 컴파일 타임에 상수의 초깃값을 알 수있게 해주는 키워드 입니다.

일단 const 부터 확인하겠습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
using namespace std;
int value(const int _x) {    // 들어오는 인자를 상수로 받음
    return _x;
}
int main() {
 
    int a;    // 변수
    cin >> a;
 
    const int b = a;    // a를 통해 상수값 b를 만듬
    b = 3;                // b는 상수기 때문에 바뀔 수 없음
    value(a);
}
cs

위와 같이 사용자 입력이나 또는 인자로 들어오는 상수값은 그때마다 변하기 때문에 런타임 시간내에서만 파악이 가능하고,
이를 런타임 상수라고 합니다.
위의 예제에서 b와 value(const int _x) 의 _x는 런타임 상수입니다.

다음은 constexpr 입니다.

1
2
3
4
5
6
7
8
9
10
11
 
int main() {
 
    constexpr double a = 1.0;    //ok, a는 컴파일 시간에 double 1.0 으로 치환됩니다.
    constexpr int b = 2;    // ok, b는 컴파일 시간에 int 2 로 치환됩니다.
 
    int c;
    cin >> c;
    constexpr int d = c;    //error, c는 런타임 변수입니다. 어떤 값이 들어올지 모르니 만들 수 없어요.
    constexpr int f;        //error, 상수는 선언과 동시에 초기화 해줘야 합니다.
}
cs

const 와 constexpr의 차이점은 const 변수는 런타임까지 지연시켜 초기화가 가능하지만, constexpr 변수는 컴파일 타임에 초기화시켜야 한다는 것입니다.
컴파일 타임에 상수를 사용한다면, 조금 더 빠르고 적은 메모리로 사용할 수 있으며 배열 및 template의 인자로 사용할 수 있습니다.
(template은 컴파일 타임에 실행되기 때문에)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 
int arrayvalue(int _x) {
    return _x * 2;
}
constexpr int arrayvalue2(int _x) {
    return _x * 2;
}
int main() {
    const int i = 10;
 
    int myarray[arrayvalue(i)];        //error, 런타임내에 작동했으므로 배열값을 정할 수 없어요
    int myarray2[arrayvalue2(i)];    //ok, 컴파일타임 내에 값을 유추했으므로 배열값으로 사용 가능
 
    std::array<int, arrayvalue(i)> starray1;    //error, 위와 같은 이유
    std::array<int, arrayvalue2(i)> starray2;    //ok, 위와 같은 이유
}
cs

어떤 값의 2배를 arraysize로 사용하는 함수를 하나 만든다면, 이렇게 사용할 수 있는데 일반적인 arrayvalue 함수는 런타임시간내에 동작하기 때문에
컴파일시간내에 작동하는 배열 혹은 template의 인자로 사용될 수 없습니다.

<constexpr의 규칙>

  • constexpr 함수는 리터럴 형식만을 허용하거나 반환해야 합니다.

  • constexpr 함수는 재귀적일 수 있습니다.

  • 가상이 될 수 없습니다. 바깥의 크래스가 가상 기본 클래스를 가지고 있다면 생성자를 constexpr로 정의할 수 없습니다. ( virtual 사용 불가 )

  • 본문은 = default 또는 = delete로 정의할 수 있습니다.

  • 본문에는 goto 문이나 try 블록이 포함될 수 없습니다.

  • constexpr이 아닌 템플릿의 명시적 특수화를 constexpr로 선언할 수 있습니다.

  • constexpr 템플릿의 명시적 특수화도 constexpr일 필요는 없습니다.

다음 규칙이 Visual Studio 2017 이상의 constexpr 함수에 적용됩니다.

  • if  switch 문을 포함할 수 있으며 for, 범위 기반 for, while, do-while을 포함한 모든 반복문을 포함할 수 있습니다.

  • 지역 변수 선언을 포함할 수 있지만 변수는 초기화되어야 하고 리터럴 형식이여야 하며 정적 또는 쓰레드 로컬일 수 없습니다. 로컬로 선언된 변수는 const일 필요가 없으며 변경할 수 있습니다.

  • Constexpr 비정적 멤버 함수는 암시적으로 const될 필요가 없습니다.

인용 : https://docs.microsoft.com/ko-kr/cpp/cpp/constexpr-cpp?view=vs-2017


<강한 열거형 (strongly-typed enum class)>

열거형은 정수 상수 집합으로 정의된 열거자의 집합입니다.

1
2
3
enum A : int;    //스코프가 없는 열거형을 만듭니다. 타입은 int
enum class B;    //스코프가 있는 열거형을 만듭니다. 타입은 int로 기본
enum class C : short//스코프가 있는 열거형을 만듭니다. 타입은 short
cs


스코프가 있는 열거형은 enum class 이며 꼭, 스코프 연산자를 통해 접근해야 합니다.
1번의 enum A : int 는 enum A 와 같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
enum A : int {        
    E_A_A = 1,
    E_A_B = 2,
    E_A_C = 3
};
enum class B {
    E_B_A = 1,
    E_B_B = 2,
    E_B_C = 3
};
int main() {
    A enumA = E_A_A;    //ok, E_A_A 로 저장합니다.
    B enumB = E_B_A;    //error, E_B_A는 어디에도 없습니다.
    B enumC = B::E_B_A;    //ok, B 열거형 안에 있는 E_B_A 로 저장합니다.
}
cs

열거형은 캐스팅을 통한 변환도 가능합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
enum A : int {        
    E_A_A = 1,
    E_A_B = 2,
    E_A_C = 3
};
enum class B {
    E_B_A = 1,
    E_B_B = 2,
    E_B_C = 3
};
int main() {
    A enumA = static_cast<A>(1);    //ok, enumA 는 A 열거형의 E_A_A 값입니다.
    B enumB = static_cast<B>(2);    //ok, enumB 는 B 열거형의 E_B_A 값입니다.
    int temp = 100000;
    B enumC = static_cast<B>(temp);    //ok, 그러나 B엔 100000과 매칭되는 값이 없습니다. 아마 버그가 날겁니다.
}
cs

다음은 사용자 정의 타입에 대한 내용인
explicit 와 = delete , = default , override 에 대해 작성하겠습니다.