상세 컨텐츠

본문 제목

[C++11] constexpr

C++/Modern C++(11, 14, 17, 20)

by deulee 2023. 8. 25. 20:25

본문

`constexpr`는 C++11에서 도입된 키워드로, "컴파일 시간"에 평가될 수 있는 "표현식(Expressions)"을 나타내는 역할을 한다.

 

컴파일러는 실행 시간이 아닌 컴파일 시간에 값을 계산하거나 함수를 호출할 수 있게 된다.

 

`constexpr`은 주로 다음 두 가지 상황에서 사용된다.

  1. `constexpr` 변수
  2. `constexpr` 함수

그럼 차례대로 알아가 보도록 하자.


Constexpr 변수

  • 변수 선언 시 초기화: `constexpr` 변수 선언할 때 초기화 식이 컴파일 시간에 평가될 수 있다면, 해당 변수는 컴파일 시간 상수가 된다. 이 변수는 실행 시간이 아닌 컴파일 시간에 계산되며, 이후에 다른 `constexpr` 표현식에서 사용될 수 있다.
#include <iostream>

class Rect {
private:
	int m_width;
	int m_height;
public:
	constexpr Rect(int width, int height) : m_width(width), m_height(height)
	{}

	constexpr int getArea() const {
		return m_width * m_height;
	}
};

int main(void)
{
	// Rect라는 객체를 constexpr로 호출 할 수 있다.
	// constexpr 생성자가 존재함.
	constexpr Rect rObj = Rect(10, 10);
	rObj.getArea(); // const로 선언된 함수를 호출 할 수 있다.

	constexpr int x = 100;
	constexpr float y{40.8};
	constexpr int z; // error -- 초기화되지 않음
	int j = 0;
	constexpr int t = j; // error -- j는 상수표현식이 아님.
	const int l = 0;
	constexpr int& c = l; // error -- `c`는 "상수 식"으로만 선언되어야 함.
	return 0;
}

하나하나 읽어보도록 하자.

 

우선 `constexpr int z` 구문을 보면 초기화되어 있지 않아 컴파일 시간에 상수성을 가지고 있지 않다. 따라서 에러.

 

그리고 "일반 변수"로는 `constexpr` 변수를 초기화할 수 없다. 그 이유는 "일반 변수" 변수는 런타임에서 초기화가 이루어진다는 것이다.

 

또 다른 중요한 점은, "참조"`constexpr`로 선언되려면 다음의 두 가지 조건을 만족해야 한다.

  • 참조 객체가 "상수 식"으로 초기화되어야 한다.

  • 초기화 되는 동안 발생하는 어떠한 "암시적 변환""상수 식"이어야 한다.

즉, `const` 변수로는 `constexpr`로 선언된 참조 변수에 "할당될 수 없다". 왜냐하면 `const`는 런타임까지 초기화를 지연시킬 수 있기 때문이다.

 

이로써 한가지 사실을 알 수 있다.

`const`는 `constexpr`이 될 수 없지만, 모든 `constexpr` 변수는 `const` 속성을 가진다.

Constexpr 함수

  • 함수 정의 시 사용: 함수에 `constexpr` 한정자를 붙여 정의하면, 해당 함수는 컴파일 시간에 호출될 수 있는 함수로 간주된다. 이 함수는 반드시 컴파일 시간에 평가될 수 있는 인자로 호출되어야 한다.
#include <iostream>

constexpr int square(int x)
{
	return x * x;
}

int square2(int x)
{
	return x * x;
}

int main(void)
{
	constexpr int a = square(2); // mov DWORD PTR [rbp-4], 4
	int b = square2(2); // mov edi, 2
						// call square2(int)
						// mov DWORD PTR [rbp-8], eax
	return 0;
}

아래는 어셈블리 코드다.

 

위의 어셈블리 코드를 보면 `constexpr`로 지정된 함수는 컴파일 단계에서 계산이 되기 때문에 런타임에서 함수가 호출되지 않는 것을 볼 수 있다. ※ 불필요한 스택 프레임 생성 안 함.

 

컴파일 타임에 알려지는 값들의 이점은 읽기 전용 메모리에 배치된다. 읽기 전용 메모리는 변경이 되지 않는 보장을 하기 때문에 더욱 효율적인 상수성의 수행이 가능하다.

컴파일 타임에 값이 계산되어 프로그램 실행 속도가 빨라지고 메모리 사용량이 줄어든다.

Constexpr 함수 주의할 점

함수가 `constexpr`로 선언되어 있다고 해서 무조건 컴파일 타임에 리턴값이 산출되는 것은 아니다.

`constexpr` 함수는 입력되는 인자 값이 컴파일 시점 상수가 아니면은 일반 함수처럼 동작한다.

그리고 다음은 함수 조건이다.

#include <iostream>

constexpr int NotConstexpr(int a, int b)
{
	goto NOT; // goto문 사용 금지

	try{} // try 사용 금지
	catch(...){}

	thread_local int t_var; // thread_local 변수 사용 금지
	static int s; // 정적 변수 금지
	int nein; // 초기화되지 않은 변수 정의 불가
	USER user; // 리터럴 타입 아닌 변수 정의 불가
NOT: // 레이블 사용 불가..
// 등등
}

이 외에도 다양한 제약 조건들이 있다.

  • `virtual` 함수 불가
  • `constexpr` 리터럴 타입만 받아들이거나 리턴
  • `constexpr` 함수는 재귀적일 수 있다.

 

 

 

'C++ > Modern C++(11, 14, 17, 20)' 카테고리의 다른 글

[C++11] Strongly-typed enums  (0) 2023.08.26
[C++11] Attributes  (0) 2023.08.25
[C++11] Delegating Constructors  (0) 2023.08.25
[C++11] User-defined literals  (0) 2023.08.25
[C++11] Explicit virtual overrides  (0) 2023.08.25

관련글 더보기