일반적으로 함수는 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)는 구현 종속적 값을 계산한다고 상상할 수 있다.
'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 |