- constexpr은 const처럼 변수에 적용 할 수 있으며 해당 변수에 대한 변경을 시도하면 컴파일러는 에러를 발생한다. const와는 다르게 생성자에 적용 할 수 있다.
- constexpr로 지정된 값이나 리턴 값이 상수이며 컴파일 타임에 계산된다. 템플릿 인수 및 배열 선언 같은 const 정수 값이 사용되는 곳에 constexpr 정수 값이 사용 될 수 있다.
- 컴파일 타임에 값이 계산되면 프로그램 실행 속도가 빨라지며 메모리 사용량이 줄어든다.
constexpr 리턴 값
constexpr 함수의 리턴 값이 constexpr 속성을 가지려면 리터럴 타입이어야 한다.
const int64_t const_mypow(int64_t itarget, int isquare)
{
if (isquare <= 1) return itarget;
return itarget * const_mypow(itarget, isquare - 1);
}
// constexpr의 리턴 값을 가지는 리터럴 함수
constexpr int64_t constexpr_mypow(int64_t itarget, int isquare)
{
if (isquare <= 1) return itarget;
return itarget * constexpr_mypow(itarget, isquare - 1);
}
int _tmain(int argc, _TCHAR* argv[])
{
std::array<int, 256> arr; // 가능 = 상수
std::array<int, const_mypow(2, 16)> const_arr; // error C2975 컴파일 상수식이 필요합니다.
std::array<int, constexpr_mypow(2, 16)> constexpr_arr; // 가능 = constexpr 리턴 함수
return 0;
}
constexpr 변수
const와 constexpr 변수의 가장 큰 다른점은 const는 런타임까지 초기화를 지연 할 수 있지만 constexpr는 컴파일 타임에 초기화 되어야 한다.
class Rect
{
public:
constexpr Rect(int width, int height) :m_iwidth(width), m_iheight(height)
{}
constexpr int getArea() const { return m_iwidth * m_iheight;}
private:
int m_iwidth;
int m_iheight;
};
// Rect라는 객체를 constexpr로 호출 할 수 있습니다.
// constexpr 생성자가 존재하기 때문에 가능합니다.
constexpr Rect ce_rect = Rect(10, 10);
ce_rect.getArea(); // const로 선언된 함수를 호출 할 수 있습니다.
constexpr int x = 205
constexpr float y{ 15 };
constexpr int i; // Error 초기화되지 않음
int j = 0;
constexpr int k = j + 1; // Error j는 상수표현식이 아닙니다.
constexpr 함수
constexpr 함수에서 주의 할 점은 constexpr 함수로 선언되어 있다고 해서 무조건 컴파일 타임에 리턴값이 산출되지 않는다.
// mypow는 constexpr 함수입니다.
// 전달 받은 인자가 runtime에 초기화된다면 일반 함수 처럼 동작
// 전달 받은 인자가 컴파일 타임에 초기화된다면 일반 함수 처럼 동작
constexpr int64_t mypow(int64_t itarget, int isquare)
{
if (isquare <= 1) return itarget;
return itarget * mypow(itarget, isquare - 1);
}
int _tmain(int argc, _TCHAR* argv[])
{
// 런타임에 초기화되는 randow 변수
auto current = std::chrono::system_clock::now();
auto duration = current.time_since_epoch();
std::mt19937_64 getMT(std::chrono::duration_cast<std::chrono::milliseconds>(duration).count());
std::uniform_int_distribution<int64_t> uniformDist(1, 10);
// constexpr 함수 mypow를 런타임에 초기화되는 인자로 호출
// 일반 함수처럼 동작 합니다.
auto value1 = mypow(uniformDist(getMT), static_cast<int>(uniformDist(getMT)));
// constexpr 함수 mypow를 constexpr 인자로 호출 constexpr 값을 리턴
constexpr auto value2 = mypow(10, 2);
std::array<int, value1> array1;
std::array<int, value2> array2;
return 0;
}
constexpr c++14 개선 사항
// c++ 11에서는 한줄의 리턴식으로 표현 해야 하는 제한이 있습니다.
constexpr int64_t mypow(int64_t itarget, int isquare)
{
return (isquare <= 1) ? itarget : itarget * mypow(itarget, isquare - 1);
}
// c++ 14에서는 한줄로 표현하는 제한이 사라졌습니다.
constexpr int64_t mypow(int64_t itarget, int isquare)
{
if (isquare <= 1) return itarget;
return itarget * mypow(itarget, isquare - 1);
}
// 루프를 통해서도 구현 가능
constexpr int64_t mypow(int64_t itarget, int isquare)
{
for (int i = 1; i < isquare; i++)
{
itarget *= itarget;
}
return itarget;
}
constexpr함수 조건
- constexpr 함수는 리터럴 타입만 받아들이거나 리턴한다.
- constexpr 함수는 재귀적일 수 있다.
- virtual 함수가 될 수 없다.
- Body에서 가능한 구문
- null Statement
- static_assert
- typedef, using
- 함수가 생성자가 아니라면 하나의 리턴 구문 필요함
- 등등...
- Body에서 불가능한 구문
- goto
- try-block
- 초기화 수행이 없는 변수 정의
- 리터럴타입이 아닌 변수 정의
- Static 변수
- tls 변수(thread local storage)
- 등등...
// constexpr 함수에서 사용하지 못하는 구문을 정리합니다.
constexpr int64_t NotConstexprFunc(int64_t itarget, int isquare)
{
goto FAIL; // goto문을 사용할 수 없습니다.
try{} // try 구문 사용 할 수 없습니다.
catch(...){}
thread_local int t_var; // thread_local 변수는 사용할 수 없습니다.
static int s_var; // static 변수 사용 할 수 없습니다.
CUser user; // 리터럴 타입아닌 변수 정의 불가
double var; // 초기화 되지 않은 변수 정의 불가
FAIL: // 레이블을 사용할 수 없습니다.
}
'프로그래밍 언어 > C++' 카테고리의 다른 글
C++ 클래스 타입 업/다운 캐스팅 (Up-DownCasting) (0) | 2022.07.22 |
---|---|
C++ 명시적 형변환/캐스팅 (explicit) (0) | 2022.07.22 |
C++ 클래스 상속 불가 및 함수 오버라이딩 불가 (final) (0) | 2022.07.21 |
C++ 중첩 클래스 (Nested Class) (0) | 2022.07.20 |
C++ 친구 클래스 및 함수 (friend) (0) | 2022.07.20 |