: 함수 내에서 다른 constexpr 함수를 반환하지 않을 경우, constexpr 붙이자.
컴파일에 처리하든, 런타임에 처리하든 알아서 처리하는 구조로 함수를 만들자.
1) constexpr & 반환의 경우.
: 지금의 경우, 반환하는 것이 lvalue 이기 때문에 상수 처리되지 않는다.
2) 이 때는 반드시 상수로 처리하는 것이기 때문에 외부에서 set하고 있어서 에러 발생이다.
3) 번외
: 이렇게 하게 되면, func 함수는 상수식이기 때문에 템플릿 인자로 보낼 수 있다.
: 객체로 constexpr 을 설정할 수 있다.
특징
: c++ 핵심 가이드라인에 작성됨
1) 가상 함수 사용 못한다.
2) 가상 상속 못한다.
3) 반드시 생성자를 두어야 하고, 생성자의 인자는 모두 리터럴이어야 한다.
출처 : 씹어먹는 c++
constexpr 객체는 가상함수와 const 함수는 호출이 안된다.
신기하게도 constexpr을 지우면, func 함수와 SetX 함수는 호출이 되지만,
이제는 상수가 아니기 때문에 template에 인자로 전달 못한다.
#include <vector>
#include <algorithm>
#include <map>
#include <string>
#include <iostream>
#include <queue>
using namespace std;
#include <iostream>
class Vector {
public:
constexpr Vector(int x, int y) : x_(x), y_(y) {}
constexpr int x() const { return x_; }
constexpr int y() const { return y_; }
virtual void func() {};
void SetX(int _x) { x_ = _x; }
private:
int x_;
int y_;
};
constexpr Vector AddVec(const Vector& v1, const Vector& v2) {
return { v1.x() + v2.x(), v1.y() + v2.y() };
}
template <int N>
struct A {
int operator()() { return N; }
};
int main() {
constexpr Vector v1{ 1, 2 };
constexpr Vector v2{ 2, 3 };
// constexpr 객체의 constexpr 멤버 함수는 역시 constexpr!
A<v1.x()> a;
std::cout << a() << std::endl;
// AddVec 역시 constexpr 을 리턴한다.
A<AddVec(v1, v2).x()> b;
std::cout << b() << std::endl;
}
: constexpr 키워드 자체가 상수성을 부여하는 걸까?
-> 반드시 상수성이 주어지는 것은 아니다.
: constexpr 함수는 상수인가에 대해서
-> constexpr 함수는 반드시 상수가 아니고, 인자가 리터럴 상수일 때, 컴파일 타임에 실행하고 , 변수 일 경우에는 런타임에 처리한다.
확인할 수 있는 예시 코드
: 씹어먹는 c++ 참고
-> 즉 리터럴 값이 인자로 들어온다면 반환값이 상수로 처리된다는 것이다.
-> 변수를 넣으면 컴파일 처리되지 않으므로 템플릿에서 오류 발생한다.
-> 즉 이때는 반환값이 상수로 처리되지 않았다는 것을 의미하게 된다.
: 모던 이펙티브 c++ 항목 15
: 컴파일 시간의 의미를 가짐.
-> 컴파일 시간에 처리되는 상수
함수의 인자 값을 컴파일 시간에 결정할 수 있으면 "컴파일 시간에 함수를 실행함." 그리고 이때는 상수 타입으로 반환된다.
그렇지 않다면, "실행 시간에 함수를 실행함."
- 위의 내용에 의해 problem코드에서 int n = add(1,1) 를 호출한다면, 인자인 1과 1 이 상수이기 때문에 컴파일 시간에 함수를 실행함.
- 하지만 problem 코드를 인용해 int n = add(aa , 5) 일 경우는 , aa는 변수이기 때문에 실행시간에 함수를 실행함.
1) 컴파일시간에 실행 할때는 리터럴 상수가 인자로 들어올 때
2) 런타입 시간에 실행 할때는 변수가 인자로 들어올 때
- template 파라미터의 특성을 이용해 실행시간에 처리되는지?
컴파일 시간에 함수 실행되는지 알아보자.
template <typename T, int N>
struct Buffer
{
T data[N];
}
constexpr int add(int a, int b)
{
return a + b;
}
int main()
{
int x = 1;
Buffer<int, 1024> b1;
Buffer<int ,x > b2; // error : x는 실행할때 알기 때문.
Buffer<int, add(1, 2)> b3;
Buffer<int ,add(x, 2)> b4; // error : add(x, 2)는 문제없지만, 실행할때 호출되는 것이므로, 오류 발생함.
}
-> error가 발생한 이유
: add(x, 2) 에서 x는 변수이므로, 이 때는 런타임에 add가 처리됨.
--> template은 컴파일 시간에 처리되어야 하기 때문에 에러 발생함.
가) consterpr로 함수를 만들어보고,
리터럴 상수를 인자로 넣어서 call 하자.
나) 변수를 인자로 넣어서 call 하자.
: add(aa, 5)는 실행시간에 실행됨.
: add(1,4)는 컴파일 시간에 실행됨.
-> 즉 리터럴값도 들어갈수 있고, 변수도 들어갈 수 있다!
라) constexpr의 타입이름을 출력해보자.
template은 struct 형식으로, struct내의 배열 변수의 크기를 결정하는
내용으로 만들어보아라.
힌트
결과
-> constexpr에 변수가 들어갔기 때문에, 런타임에 실행
--> 템플릿의 경우, 컴파일 시간에 타입이 정해져야 하는데...
N값에 변수가 들어가므로, 당연히 빌드 오류가 발생됨을 확인할 수 있음.
리터럴값인 경우, 컴파일 상수로 반환되면서 템플릿인자로 사용할 수 있게 되었다.
마) constexpr은 상수인지를 확인하기 위해서 ,
레퍼런스 타입으로 함수를 만들고, 외부에서
값 대입을 해보아라..
1) 일단을 실험을 위해서 return a + b; 가 아니라 return a; 로 변경하라. 인자로 참조 타입으로 받아라.
-> 4에서 17로 변경됨을 확인함.
2) const 함수로 만들어서 확인하라 .
3) constexpr로 만들어서 확인하라.
-> 상수가 아님을 확인할 수 있음.
-> a가 리터럴이 아니기 때문에 상수타입으로 결정되지 않는다!
-> 변수가 들어가서 런타임에 실행되어서 상수 를 반환한 것이 아니다.
어쨋든 상수 함수라고는 할 수 없는 상태임.
--> 일반함수로 동작함.
4) 한번 더 확인하는 예시코드
5) 추가
: 타입을 확인해보자.
: constexpr 함수는 컴파일 시간에 처리가 가능하면 ,
컴파일 시간에 함수를 실행해버림.