Program Language/C & C++

[C++] constexpr 함수

야곰야곰+책벌레 2022. 1. 13. 10:30
728x90
반응형

일반적으로 함수는 compile-time에서 평가할 수 없으므로 상수 표현식에서 호출할 수 없다. constexpr함수를 지정함으로써 상수 표현식이 인수로 주어지면 상수 표현식에서 사용할 수 있기를 원한다는 것을 나타낸다.

constexpr int fac(int n)
{
	return (n>1) ? n∗fac(n−1) : 1;
}
constexpr int f9 = fac(9); // must be evaluated at compile time

constexpr이 함수 정의에서 사용될 때, "상수 표현식이 인수로 주어지면 상수 표현식을 사용할 수 있어야 한다"라는 의미다. 객체 정의에서 사용하면, compile-time에 intiailizer를 평가한다는 의미다.

void f(int n)
{
	int f5 = fac(5); // may be evaluated at compile time
	int fn = fac(n); // evaluated at run time (n is a var iable)
	constexpr int f6 = fac(6); // must be evaluated at compile time
	constexpr int fnn = fac(n); // error : can’t guarantee compile-time evaluation (n is a var iable)
	char a[fac(4)]; // OK: array bounds must be constants and fac() is constexpr
	char a2[fac(n)]; // error : array bounds must be constants and n is a var iable
	// ...
}

compile-time에 평가되려면 함수는 적절하게 단순해야 한다. constexpr 함수는 단일 return문으로 구성되어야 한다. 루프 및 지역 변수가 허용되지 않는다. 또한 constexpr함수에는 부작용이 없을 수 있다. 즉, constexpr함수는 순수 함수다.

int glob;
constexpr void bad1(int a) // error : constexpr function cannot be void
{
	glob = a; // error : side effect in constexpr function
}
constexpr int bad2(int a)
{
	if (a>=0) return a; else return −a; // error : if-statement in constexpr function
}
constexpr int bad3(int a)
{
	sum = 0; // error : local var iable in constexpr function
	for (int i=0; i<a; +=i) sum +=fac(i); // error : loop in constexpr function
	return sum;
}

constexpr 생성자에 대한 규칙은 멤버의 단순 초기화만 허용된다.

constexpr 함수는 재귀 및 조건식을 허용한다. 이것을 정말 원한다면 constexpr 함수로 거의 모든 것을 표현할 수 있음을 의미한다. 그러나 constexpr 함수의 사용을 의도한 비교적 간단한 작업으로 제한하지 않는 한 디버깅이 불필요하게 어렵고 컴파일 시간이 더 오래 걸린다는 것을 알게 될 것이다.

constexpr과 참조

constexpr함수는 부작용을 가질 수 없으므로 로컬이 아닌 객체에 사용하는 것은 불가능하다. 그러나 constexpr함수는 로컬 객체에 쓰지 않는 한 로컬이 아닌 객체를 참조할 수 있다.

constexpr int ftbl[] { 1, 2, 3, 5, 8, 13 };
constexpr int fib(int n)
{
	return (n<sizeof(ftbl)/siz eof(∗ftbl)) ? ftbl[n] : fib(n);
}

constexpr함수는 참조 인수를 사용할 수 있다. 물론 그러한 참조를 통해 쓸 수는 없지만 const 참조 매개변수는 그 어느 때보다 유용하다. 

template<> class complex<float> {
public:
	// ...
	explicit constexpr complex(const complex<double>&);
	// ...
};

이것은 사용할 수 있게 해 주는데,

constexpr complex<float> z {2.0};

const 참조 인수를 보유하기 위해 논리적으로 구성된 임시 변수는 단순히 컴파일러 내부 값이 된다.

constexpr함수가 참조 또는 포인터를 반환하는 것이 가능하다.

constexpr const int∗ addr(const int& r) { return &r; } // OK

그러나 그렇게 하면 상수 표현식 평가의 일부로서 constexpr함수의 기본적인 역할에서 멀어지게 된다. 특히, 그러한 함수의 결과가 상수 표현식인지 여부를 판별하는 것은 상당히 까다로울 수 있다.

static const int x = 5;
constexpr const int∗ p1 = addr(x); // OK
constexpr int xx = ∗p1; // OK
static int y;
constexpr const int∗ p2 = addr(y); // OK
constexpr int yy = ∗y; // error : attempt to read a var iable
constexpr const int∗ tp = addr(5); // error : address of temporar y

조건부 평가

constexpr함수에서 사용되지 않는 조건식의 분기는 평가되지 않는다. 이는 취하지 않은 분기에 런타임 평가가 필요할 수 있음을 의미한다.

constexpr int check(int i)
{
	return (low<=i && i<high) ? i : throw out_of_rang e();
}
constexpr int low = 0;
constexpr int high = 99;
// ...
constexpr int val = check(f(x,y,z));

low와 high는 compile-time에 알려져 있지만 design-time에는 알려지지 않은 구성 매개변수이고 f(x,y,z)는 구현 종속적 값을 계산한다고 상상할 수 있다.

728x90
반응형

'Program Language > C & C++' 카테고리의 다른 글

[C++] 리소스 관리  (0) 2022.01.13
[C++] 미리 정의된 매크로  (0) 2022.01.13
[C++] 함수의 지정자와 수정자  (0) 2022.01.13
[C++] 명시적 유형 변환  (0) 2022.01.12
[C++] lambda expression  (0) 2022.01.07