const*, constexpr**

C++ 2019. 12. 25. 09:25
반응형
 

const (C++)

const (C++)const (C++) 이 문서의 내용 --> 데이터 선언을 수정할 때 합니다 const 키워드는 개체 또는 변수는 수정할 수를 지정 합니다.When modifying a data declaration, the const keyword specifies that the object or variable is not modifiable. 구문Syntax const declaration ; member-function const ; cons

docs.microsoft.com

데이터 선언을 수정할 때, const 키워드는 개체 또는 변수를 수정할 수 없도록 지정합니다.

 

구문

1
2
const declaration ;
member-function const ;
 

 

 

const 값

const 키워드는 변수 값이 상수임을 지정하고 프로그래머가 변수 값을 수정하지 못하도록 컴파일러에 지시합니다.

1
2
3
4
5
6
int main() {
   const int i = 5;
   i = 10;   // C3892
   i++;   // C2105
}
 

4번줄 5번줄에서 3번줄에서 const로 선언된 i를 수정하려 하니 터지는 다는 것을 보여준다.

 

 

 

 

 

C++에서는 상수 값을 정의하기 위해 #define 전 처리기 지시문 대신 const 키워드를 사용할 수 있습니다. const로 정의 된 값은 타입 검사를 받으며 상수 표현식 대신 사용할 수 있습니다.

 

C++에서 다음과 같이 const 변수를 사용하여 배열의 크기를 지정할 수 있습니다.

 

1
2
3
4
// compile with: /c
const int maxarray = 255;
char store_char[maxarray];  // allowed in C++; not allowed in C
 

 

4번줄 주석을 보면, C++ 에선 되는데 C에선 안된다는 말을 한다....???

C에서 예제와 같은 코드

실험결과 아예 빨간줄을 긋는 것으로 나타난다.

 

 

 

 

 

 

C에서 상수 값은 기본적으로 외부 연결이므로 소스 파일에만 나타날 수 있습니다. C++에서 상수 값은 내부 링크로 기본 설정되어 있으므로 헤더 파일에 나타날 수 있습니다.

 

const 키워드는 포인터 선언에서도 사용할 수 있습니다.

1
2
3
4
5
6
7
int main() {
   char *mybuf = 0*yourbuf;
   char *const aptr = mybuf;
   *aptr = 'a';   // OK
   aptr = yourbuf;   // C3892
}
 

 

3번줄은 그냥 char 포인터이며, 4번줄이 char형의 const 포인터이다.

4번줄의 포인터는 상수값으로 취급되어 변경될 수 없다.

 

 

 

 

 

const로 선언된 변수에 대한 포인터는 const로 선언된 포인터에만 할당할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
int main() {
   const char *mybuf = "test";
   char *yourbuf = "test2";
   printf_s("%s\n", mybuf);
 
   const char *bptr = mybuf;   // Pointer to constant data
   printf_s("%s\n", bptr);
 
   // *bptr = 'a';   // Error
}
 
 

4번줄에 정의된 const char 형의 포인터를 8번줄의 같은 형으로 받는 모습을 보여준다.

바로 위 예제와 다른점은 const char의 포인터형과 char const 포인터는 엄연히 다르며, 전자는 const화 된 char를 뜻하고 후자는 const화 된 char의 포인터를 뜻한다. 즉, const char 포인터는 수정이 가능하지만 char const는 상수화 된 값이라 수정이 불가능하다.

 

 

 

 

상수 데이터에 대한 포인터를 함수 매개 변수로 사용하여 함수가 포인터를 통해 전달 된 매개 변수를 수정하지 못하도록 할 수 있습니다.

const로 선언 된 객체의 경우 상수 멤버 함수 만 호출 할 수 있습니다. 이렇게하면 상수 개체가 수정되지 않습니다.

1
2
birthday.getMonth();    // Okay
birthday.setMonth( 4 ); // Error
 

 

 

 

const화 되지 않은 객체에 대해 상수 또는 비 상수 멤버 함수를 호출할 수 있습니다. const 키워드를 사용하여 멤버 함수를 오버로드 할 수도 있습니다.-이렇게 하면 const화 된 객체 및 const화 되지 않은 객체에 대해 다른 버전의 함수를 호출 할 수 있습니다.

 

 

 

자체예제_

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class A 
{
public:
    void func1()
    {
        cout << "A class func1" << endl;
    }
 
    void func1() const
    {
        cout << "A class const func1" << endl;
    }
 
public:
    A() {}
    ~A() {}
};
 
int main() 
{
    A a;
    a.func1();
 
    const A ac = A();
    ac.func1();
}
 
 

 

자체예제 실행결과_

1
2
class func1
class const func1
 

 

자체 예제 결과 해당 설명은...

  • 같은 이름으로 선언된 두 함수 중 하나에 const 지정자가 쓰인 경우를 만든다. (클래스 멤버함수 참고)
  • const로 선언된 객체에선 const 지정자가 쓰인 함수를 호출한다. -> 9번줄 void func1() const 호출
  • 그렇지 않은 객체에선 const 지정자가 쓰이지 않은 함수를 호출한다. -> 4번줄 void func1() 호출

 

 

 

 

const 키워드로 생성자 또는 소멸자를 선언할 수 없습니다.

실험결과임

 

const 멤버 함수

 

const 키워드로 멤버 함수를 선언하면 함수가 호출된 객체를 수정하지 않는 "읽기 전용(read-only)" 함수임을 지정합니다. 상수 멤버 함수는 비정적(non-static) 데이터 멤버를 수정하거나 상수가 아닌 멤버 함수를 호출 할 수 없습니다.

 

상수 멤버 함수를 선언하려면 인수 목록의 닫는 괄호 뒤에 const 키워드를 추가하십시오. const 키워드는 선언과 정의 모두에 필요합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Date
{
public:
   Date( int mn, int dy, int yr );
   int getMonth() const;     // A read-only function
   void setMonth( int mn );   // A write function; can't be const
private:
   int month;
};
 
int Date::getMonth() const
{
   return month;        // Doesn't modify anything
}
void Date::setMonth( int mn )
{
   month = mn;          // Modifies data member
}
int main()
{
   Date MyDate( 741998 );
   const Date BirthDate( 1181953 );
   MyDate.setMonth( 4 );    // Okay
   BirthDate.getMonth();    // Okay
   BirthDate.setMonth( 4 ); // C2662 Error
}
 
 

 

 

 

C 와 C++ 에서 const 차이점

변수를 C 소스 코드 파일에서 const로 선언하면 다음과 같이됩니다.

1
const int i = 2;
 

 

그런 다음, 이 변수를 다음과 같이 다른 모듈에서 사용할 수 있습니다.

1
extern const int i;
 

 

그러나 C++에서 동일한 동작을 얻으려면, const 변수를 다음과 같이 선언해야합니다.

1
extern const int i = 2;
 

 

 

 

C 소스 코드 파일에서 extern 변수를 선언하여 C 소스 코드 파일에서 사용하려면 다음을 사용하

십시오.

1
extern "C" const int x=10;
 

 

C++ 컴파일러에 의한 네임 맹글링(name mangling)을 방지합니다.

 

*

name mangling_

자자.... 이거 함수와 관련된 Post가 필요한데 아직 작성하지 못했다...;ㅠㅠ

차후에 연결지을 포스팅이 완성될 것이다!!!!!

점점 MSDN 볼 항목이 많아지니 좀만내려가면 된다ㅇㅇㅇ!!!!!!!!!!!!!!!!!

 

 

 

참고

멤버 함수의 매개 변수 목록을 따를 때 const 키워드는 함수가 호출된 객체를 수정하지 않도록 지정합니다.

 

 

const에 대한 자세한 내용은 다음 항목을 참조하십시오.

 

 

 

 

constexpr (C++)

constexpr 키워드는 C ++ 11에 도입되었으며 C ++ 14에서 개선되었습니다. 상수 표현(constant expression)을 의미합니다.

 

const와 같은 점은 변수에 적용하여 코드가 값을 수정하려고하면 컴파일러 오류가 발생합니다.

const와 다른 점은 constexpr은 함수와 클래스 생성자에도 적용될 수 있습니다. constexpr은 값 또는 반환 값이 상수임을 나타냅니다. 가능한 경우 컴파일 타임에 계산됩니다. -> 성능(런타임) 향상, 컴파일 시 고생!! 이란 말이다.

 

constexpr 정수 값은 템플릿 인수 및 배열 선언과 같이 const 정수가 필요한 모든 위치에서 사용할 수 있습니다. 그리고 런타임이 아닌 컴파일 타임에 값을 계산할 수 있다면 프로그램 실행 속도를 높이고 메모리 사용량을 줄일 수 있습니다.

 

컴파일 타임 상수 계산의 복잡성과 컴파일 타임에 미칠 수 있는 영향을 제한하기 위해 C++ 14 표준은 상수 표현식의 유형을 리터럴 유형(literal types)으로 요구합니다.

 

 

구문

constexpr literal-type identifier = constant-expression ; 
constexpr literal-type identifier {constant-expression } ;
constexpr literal-type identifier ( params ) ;
constexpr ctor ( params );

 

Parameters

params
하나 이상의 매개 변수에서 각 매개 변수는 리터럴 유형이어야하며 그 자체가 상수 표현이어야합니다.

 

반환 값

constexpr 변수와 함수는 반드시 literal type 을 반환합니다.

literal_type_**

더보기

리터럴 유형은 컴파일 타임에 레이아웃을 결정할 수있는 유형입니다. 다음은 리터럴 유형입니다.

  • void
  • scalar 형
  • references 참조 형
  • void, scalar, 참조 형의 배열
  • trivial 소멸자가 있는 클래스와 생성자를 이동하거나 복사하지 않는 하나 이상의 constexpr 생성자를 가진 클래스. 또한 모든 비정적 데이터 멤버 및 기본 클래스는 리터럴 형식이어야하며 volatile이 아니어야합니다.

쉽게 말하면, 컴파일 시간에 처리되는 constexpr의 특성에 맞추어 컴파일 시간에 처리될 수 있어야 함을 의미한다.

 

후에 해당 키워드를 설명하는 포스트가 있을 시 링크를 걸겠다.



출처: https://mtding.tistory.com/18 [M_TDING]

constexpr 변수

const 변수와 constexpr 변수의 주요 차이점은 런타임까지 const 변수의 초기화를 연기할 수 있다는 것입니다. 컴파일 타임에 constexpr 변수를 반드시 초기화 합니다. 모든 constexpr 변수는 const입니다.

  • 변수가 리터럴 타입이고 초기화 된 경우 constexpr을 사용하여 변수를 선언할 수 있습니다. 초기화가 생성자에 의해 수행되면 생성자는 constexpr로 선언되어야합니다.
  • 참조하는 객체가 상수 표현식에 의해 초기화되고 초기화 중에 호출되는 암시적 변환이 상수 표현식이라면 참조는 constexpr로 선언될 수 있습니다.
  • constexpr 변수 또는 함수의 모든 선언에는 constexpr 지정자가 있어야 합니다. 

3번 항목은 constexpr 을 표기해야 한다는 비교적 당연한 이야기이다. 하지만 1번 항목과 2번 항목에 대한 예제는 존재하지 않아 변수 선언에 대해 나름 예제를 만들어 보았다.

 

 

2번 항목 예제_

1
2
constexpr float j = 0;
constexpr int k = j + 1;
 

 

float 가 int 로 암시적 형변환이 이루어질 수 있느냐는 것이다. 해당 코드는 컴파일은 문제 없이 잘 되었다.

!아직 제대로된 예제가 아니니 주의바람! - 쏠까 당최 뭔 코든지 생각이 나질 않는다.

 

 

1번 항목 예제_

1
2
3
4
5
6
7
8
9
10
11
class XX
{
public:
    constexpr XX() : x(0) {};
};
 
 
int main() 
{
    constexpr XX tx = XX();
}
 

위 코드는 정상적으로 진행된다. 만약 4번줄, 생성자에서 constexpr 지정자가 빠질 경우 다음과 같은 오류가 생긴다.

 

1
2
3
4
5
6
7
8
9
10
11
class XX
{
public:
    XX() : x(0) {};
};
 
 
int main() 
{
    constexpr XX tx = XX();
}
 

 

터지는 모습

 

1
2
3
4
5
6
constexpr float x = 42.0;
constexpr float y{108};
constexpr float z = exp(53);
constexpr int i; // Error! Not initialized
int j = 0;
constexpr int k = j + 1//Error! j not a constant expression
 

 

3번줄 에 exp(5, 3) 함수가 리터럴 타입을 반환할 뿐만 아니라 constexpr로 지정되어있어야 한다.

함수에 대한 constexpr 형은 아래에 나온다.

  

 

 

 

 

constexpr 함수

constexpr 함수는 반환 값을 [소비 코드를 요구할 때](consuming code requires) 컴파일 시간에 계산할 수 있는 함수입니다. 예를 들면, 소비코드는 constexpr 변수를 초기화 하거나 비형식(non-type) 템플릿 매개변수를 제공할 때 컴파일 시간에 반환 값을 요구합니다. 매개변수가 constexpr 값인 경우 constexpr 함수는 컴파일 시간 상수를 생성합니다. constexpr 인자가 아닌 값(non-constexpr)으로 호출하거나 컴파일 타임에 해당 값이 필요하지 않은 경우 일반 함수처럼 런타임에 값을 생성합니다. (이 이중 동작은 동일한 함수의 constexpr 및 non-constexpr 버전을 작성하지 않아도 됩니다.)

 

constexpr 함수 또는 생성자는 암시적으로 인라인입니다.

constexpr 함수에는 다음 규칙이 적용됩니다.

  1. constexpr 함수는 반드시 리터럴 형식(literal_types)만을 받고 반환해야 합니다.
  2. constexpr 함수는 재귀함수일 수 있습니다.
  3. 그것은 가상함수일 수 없습니다. 둘러싸는 클래스에 어떠한 가상 기본 클래스가 있는 경우, 생성자를 constexpr로 정의할 수 없습니다. - 컴파일 시간에 결정할 수 가 없다는 뜻
  4. 함수의 구현부는 = default (기본값) 또는 = delete (비정의)로 정의할 수 있습니다.
  5. 함수의 구현부에는 goto 문이나 try 블록이 포함될 수 없습니다.
  6. constexpr가 아닌 템플릿의 명시적인 특수화는 constexpr로 선언될 수 있습니다.
  7. constexpr 템플릿의 명시적 특수화는 constexpr 일 필요는 없습니다.

 

 

 

다음 규칙은 Visual Studio 2017 이상에서 constexpr 함수에 적용됩니다.

  1. if  switch 문과 for, range-based for, while  do-while을 비롯한 모든 반복문을 포함할 수 있습니다.
  2. 로컬 변수 선언을 포함 할 수 있지만 변수는 초기화 되어야하고 리터럴 유형이어야 하며, static 또는 thread-local 일 수 없습니다. 국지적으로 선언 된 변수는 const 일 필요는 없으며 변경될 수 있습니다.
  3. constexpr 으로된 비정적(non-static) 멤버 함수는 암시적으로 const일 필요는 없습니다.

 

공식사이트에 올라와 있는 예제다.

1
2
3
4
5
6
constexpr float exp(float x, int n)
{
    return n == 0 ? 1 :
        n % 2 == 0 ? exp(x * x, n / 2) :
        exp(x * x, (n - 1/ 2* x;
};
 
 

 

해당 예제는 제귀함수를 이용한 모습을 보여준다.

 

 

 

필자는 constexpr 지정자를 사용해 함수를 꾸릴 경우 하면 안되거나, 주의사항 몇 가지를 직접 실험해보았다. 물론 오류가 떠서 컴파일을 못했지만 어떤 오류가 뜨는지 확실히 해야할 필요가 있다고 생각했다.

 

 

 

본문 3번 - 함수가 virtual인 경우

터짐1

 

본문 5번 - 구현부에서 try나 goto를 쓸 경우

터짐2

 

 

 

본문 1번 - 함수에서 사용하는 변수나 인자가 literal_type이 아니고 constexpr이 아닌 경우

터짐3

 

 

본문 2017이하 2번 - 함수 구현부에서 static, thread_local을 사용한 경우

터짐4

 

 

 

 

 

팁_

Visual Studio 디버거에서 최적화되지 않은 디버그 빌드를 디버깅 할 때 컴파일 타임에 중단점을 넣어 constexpr 함수가 평가되는지 여부를 알 수 있습니다. 중단점에 도달하면 함수가 런타임에 호출됩니다. 그렇지 않으면 컴파일 타임에 함수가 호출됩니다.

 

 

 

extern constexpr

/Zc:externConstexpr 컴파일러 옵션을 사용하면 컴파일러에서 extern constexpr을 사용하여 선언된 변수에 외부 연결을 적용합니다. 이전 버전의 Visual Studio에서는 기본값 또는 /Zc:externConstexpr-이 지정된 경우 Visual Studio는 extern 키워드가 사용되는 경우에도 constexpr 변수에 내부 연결을 적용합니다. /Zc:externConstexpr 옵션은 Visual Studio 2017 Update 15.6부터 사용할 수 있습니다. 기본적으로 꺼져 있습니다. /permissive- 옵션은 /Zc:externConstexpr을 활성화하지 않습니다.

 

 

 

예제

다음 예제는 constexpr 변수, 함수 및 사용자 정의 유형을 보여줍니다. main()의 마지막 문에서, constexpr 멤버 함수 GetValue()는 컴파일 시간에 값을 요구하지 않으므로 런타임 호출입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <iostream>
 
using namespace std;
 
// Pass by value
constexpr float exp(float x, int n)
{
    return n == 0 ? 1 :
        n % 2 == 0 ? exp(x * x, n / 2) :
        exp(x * x, (n - 1/ 2* x;
};
 
// Pass by reference
constexpr float exp2(const float& x, const int& n)
{
    return n == 0 ? 1 :
        n % 2 == 0 ? exp2(x * x, n / 2) :
        exp2(x * x, (n - 1/ 2* x;
};
 
// Compile-time computation of array length
template<typename T, int N>
constexpr int length(const T(&ary)[N])
{
    return N;
}
 
// Recursive constexpr function
constexpr int fac(int n)
{
    return n == 1 ? 1 : n*fac(n - 1);
}
 
// User-defined type
class Foo
{
public:
    constexpr explicit Foo(int i) : _i(i) {}
    constexpr int GetValue()
    {
        return _i;
    }
private:
    int _i;
};
 
int main()
{
    // foo is const:
    constexpr Foo foo(5);
    // foo = Foo(6); //Error!
 
    // Compile time:
    constexpr float x = exp(53);
    constexpr float y { exp(25) };
    constexpr int val = foo.GetValue();
    constexpr int f5 = fac(5);
    const int nums[] { 1234 };
    const int nums2[length(nums) * 2] { 12345678 };
 
    // Run time:
    cout << "The value of foo is " << foo.GetValue() << endl;
 
}
 
 

62번줄 에서는 GetValue() constexpr 꼴로 받지 않는다. 따라서, 이 이유로 인해 런타임 실행이라 설명한다.

 

요구사항

Visual Studio 2015

참고

Declarations and Definitions
const

좋아요 공감

공유하기

글 요소

반응형
Posted by Kestone Black Box
,