태크놀로지

[C++] constexpr, const 본문

C++

[C++] constexpr, const

원택 2020. 5. 26. 21:15

상수

상수란 값이 변하지 않는 불변값이다.

상수(const, constexpr)는 값이 할당되어 있어야하며, 런타임도중 값을 변경할수없다.

 

constexpr int t1; // error: must Init
const int t2 = 10;
t2 = 5; // error: cant change in running time

constexpr & const 차이점

1. constexpr 할당시기는 컴파일 시간때 확정되어있어야한다.

2. const는 컴파일시간이나 런타임 시간 아무때나 할당되도 상관없다.

※ 그 외 공통점은 상수의 특징과 동일하다.

 

void Test(int v)
{
    constexpr int a = v + 7 // error: 컴파일할때 v를 알수없기 때문에, 런타임시간에 a변수가 초기화됨으로서 constexpr를 사용했을시 컴파일이 되지않는다.
    const int a1 = v + 7 // ok: 상수변수는 일반변수로부터 초기화할수있다.(const만)
}

constexpr

필자또한 공부해가는 과정이기 때문에 [출처: 수까락의 프로그래밍 이야기, http://egloos.zum.com/sweeper/v/3147813]를 많이 참고하였다. cppReference의 자료를 잘 풀어서 설명해주셨다. 아래 코드는 cppReference에 가면 참고할수있다.

 

constexpr -> 컴파일 타임에 오류를 발생

const -> 런타임시간에 오류를 발생

 

단점: 컴파일 속도가 느려질수있다.

 

1) 변수에서의 사용

위에서 말한거와 같이 const와 constexpr의 주요 차이점은 런타임시간에 오류가 발생하냐, 컴파일시간에 발생하냐의 차이이다. constexpr은 컴파일시간에 무조건 초기화가 되어있어야 한다.

 

2) 함수에서의 사용

constexpr은 함수 반환값을 사용할때는 다음의 제약들이 따른다.

1. 가상으로 재정의된 함수가 아니여야 한다.

2. 반환값의 타입은 반드시 LiteralType이어야 한다.

3. 함수에 constexpr을 붙일경우 inline을 암시한다.

 

C++11에서는 함수본문에 지역변수를 사용할수없고, 하나의 반환 표현식만 와야하는 제약이있었는데, C++14부터는 사라졌다.

// C++11 constexpr functions use recursion rather than iteration
// (C++14 constexpr functions may use local variables and loops)
constexpr int factorial(int n)
{
    // 하나의 반환문, 멤버변수 없음
    return n <= 1 ? 1 : (n * factorial(n - 1));
}

// C++ 14에서 작동한다. (C++ 11 작동안함)
// 멤버변수 사용
// 여러개의 반환문
constexpr int factorial(int n)
{
    int result = 0;

    if (n <= 1)
        result = 1;
    else
        result = (n * factorial(n - 1));

    return result;
}

constexpr 함수는 컴파일러에게 가능하다면, 상수시간에 컴파일해달라고 요청하는 것이지만 상황에 따라 컴파일 타임에 미리 실행될 수도 있고, 그렇지 못하고 런타임에 실행될 수도 있다. (inline과 비슷함)

 

// template n이 컴파일 타임 상수인지를 테스트하기 위한 구조체이다.
template<int n>
struct constN
{
    constN() { std::cout << n << '\n'; }
};

// 함수 매개변수로 constexpr 넘기고있는데 이 경우는 런타임에 지정되는거라 오류가 발생한다.
// const를 사용하면 작동한다.
constexpr int factorial(constexpr int n)
{
    int result = 0;

    if (n <= 1)
        result = 1;
    else
        result = (n * factorial(n - 1));

    return result;
}

int main()
{
    std::cout << "4! = ";
	
    // 이 경우 a값은 컴파일시간에 a가 무엇인지 알수없기때문에 오류가 발생한다.
    int a = 4; // const or constexpr을 붙여줄 경우 잘 작동한다.
    constN<factorial(a)> out1; // computed at compile time
}

 

하지만 무작정 constexpr을 남발해서도 안된다. 함수가 절대 상수표현식으로서 평가받지 못하는 문맥을 가지는 경우에도 컴파일 에러가 발생한다.

#include <random>
constexpr int rand_test() {
    // Error C3615: constexpr 함수 'rand_test'의 결과가 상수 식이면 안됩니다.
    // 리턴값이 상수가 아니라는 말인것같다.
    return rand() % 65535;
}

 

3) 생성자 함수에서의 사용

constexpr을 찾아보게 된 원인도 unique_ptr를 만들어보다가 stl 라이브러리에 unique_ptr 생성자에 constexpr가 붙여져 있길래, 찾아보게 되었다.

 

LiteralType 클래스를 생성할때 constexpr 생성자를 사용할 수 있다.

모든 생성자 함수의 매개변수들 역시 LiteralType들이여야 한다.

※ LiteralType이란 int a = 5; 와 같이 상수값이나 지정된 값으로 초기화되어있는것 (위에서 말한 컴파일 시간에 할당된 변수들이다.)

어떤 클래스로부터 상속받지 않아야 한다. (독자적인 클래스)

 

// literal class
class conststr {
    const char* p;
    std::size_t sz;
public:
    template<std::size_t N>
    constexpr conststr(const char(&a)[N]) : p(a), sz(N - 1) {}

    // constexpr functions signal errors by throwing exceptions
    // in C++11, they must do so from the conditional operator ?:
    constexpr char operator[](std::size_t n) const
    {
        return n < sz ? p[n] : throw std::out_of_range("");
    }
    constexpr std::size_t size() const { return sz; }
};

// C++11 constexpr functions had to put everything in a single return statement
// (C++14 doesn't have that requirement)
// 소문자의 개수를 세는 함수이다. conststr을 인자로 받고있다.
constexpr std::size_t countlower(conststr s, std::size_t n = 0,
    std::size_t c = 0)
{
    return n == s.size() ? c :
        'a' <= s[n] && s[n] <= 'z' ? countlower(s, n + 1, c + 1) :
        countlower(s, n + 1, c);
}

int main()
{
    std::cout << "the number of lowercase letters in \"Hello, world!\" is ";
    constN<countlower("Hello, world!")> out2; // implicitly converted to conststr
}

main문에 "Hello, world!"라는 LteralType이 인자로 전달되고 있다.


잘못된 부분이 있다면, 댓글로 피드백 부탁드립니다.

 

출처

https://boycoding.tistory.com/156

 

C++ 02.10 - 상수 (const, constexpr, and symbolic constants)

02.10 - 상수 (const, constexpr, and symbolic constants) 상수 (constant) 상수란 그 값이 변하지 않는 불변 값이다. 지금까지 본 변수들은 모두 언제든지 값이 변할 수 있었다. int x { 4 }; // initialize x w..

boycoding.tistory.com

http://egloos.zum.com/sweeper/v/3147813

 

[C++11/14] constexpr

1. constexpr http://en.cppreference.com/w/cpp/language/constexpr 기존의 const 보다 훨씬 더 상수성에 충실하며, 컴파일 타임 함수를 이용한 성능 향상 등 충분히 깊게 이해할만한 가치가 있는 녀석이라 할 수 있�

egloos.zum.com

https://en.cppreference.com/w/cpp/language/constexpr

 

constexpr specifier (since C++11) - cppreference.com

[edit] Explanation The constexpr specifier declares that it is possible to evaluate the value of the function or variable at compile time. Such variables and functions can then be used where only compile time constant expressions are allowed (provided that

en.cppreference.com