Mutable Data Members (C++)
이 키워드는 클래스에서 static도 const도 아닌 데이터 멤버에만 적용할 수 있습니다. 데이터 멤버를 mutable로 선언하면, const 멤버 함수에서 이 데이터 멤버에 대한 값 할당 작업이 허용됩니다.
구문
1
|
mutable member-variable-declaration;
|
설명
예를 들어, 다음 코드는 m_accessCount가 mutable로 선언되었기 때문에 오류 없이 컴파일되고, 따라서 GetFlag가 const 멤버 함수라도 GetFlag에서 이를 변경할 수 있습니다.
예제_
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
// mutable.cpp
class X
{
public:
bool GetFlag() const
{
m_accessCount++;
return m_flag;
}
private:
bool m_flag;
mutable int m_accessCount;
};
int main()
{
}
|
자체예제_
일반 변수와 mutable 변수의 차이
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
class A
{
mutable int ia;
int ib;
A() : ia(0), ib(0)
{
}
void TT() const
{
ia = 3;
ib = 3; // 이 부분에서 E0137 오류가 뜬다 - 내용은 수정할 수 없다고 한다.
}
};
int main()
{
return 0;
}
|
s |
auto (C++)
초기화 된 표현식에서 선언된 변수의 유형을 유추합니다.
구문
1
2
|
auto declarator initializer;
[](auto param1, auto param2) {};
|
참고
auto 키워드는 컴파일러가 선언된 변수 또는 람다 식 매개 변수의 초기화 식을 사용하여 형식을 추론하도록 지시합니다.
다음과 같은 이점을 제공하므로 실제로 전환을 원하지 않는 한 대부분의 상황에서 auto 키워드를 사용하는 것이 좋습니다.
-
견고성_Robustness: 표현식의 유형이 변경된 경우 (함수 반환 유형이 변경된 경우 포함) 작동합니다.
- 수행_Performance: 변환(Conversion)이 발생하지 않음을 보장합니다.
- 편의성_Usability: 유형 이름 맞춤법 불량 및 오타에 대해 걱정할 필요가 없습니다.
- 효율성_Efficiency: 코딩이 더 효율적일 수 있습니다.
auto 사용을 원하지 않는 전환 사례:
- 한정자를 지정하고 아무 것도 하지 않기를 원할 때
- 템플릿 도우미 형의 표현 시 (예, valarray + valarray)
auto 키워드를 사용하려면 유형 대신 변수를 사용하여 변수를 선언하고 초기화 표현식을 지정하십시오. 또한 const, volatile, pointer (*), 참조 (&) 및 rvalue 참조 (&&)와 같은 지정자와 선언자를 사용하여 auto 키워드를 수정할 수 있습니다. 컴파일러는 초기화 표현식을 평가 한 다음, 변수 유형을 추론한 해당 정보를 사용합니다.
초기화 표현식은 할당 (등호 구문), 직접 초기화 (함수 스타일 구문), 연산자 new 표현식 또는 초기화 표현식이 범위 기반 선언문의 for-range-declaration 매개 변수가 될 수 있습니다 ( C ++) 문. 자세한 내용은이 문서 뒷부분의 초기화 프로그램 및 코드 예제를 참조하십시오.
auto 키워드는 유형에 대한 자리표시자이지만 그 자체가 유형이 아닙니다. 따라서 auto 키워드는 sizeof 및 (C ++ / CLI의 경우) typeid와 같은 캐스트 또는 연산자에서 사용할 수 없습니다.
유용성
auto 키워드는 복잡한 유형의 변수를 선언하는 간단한 방법입니다. 예를 들어, auto를 사용하여 초기화 표현식에 템플릿, 함수에 대한 포인터 또는 멤버에 대한 포인터가 포함된 변수를 선언할 수 있습니다.
또한 auto를 사용하여 변수를 람다식으로 선언하고 초기화 할 수도 있습니다. 람다 식의 형식은 컴파일러에만 알려져 있기 때문에 직접 변수의 형식을 선언할 수는 없습니다. 자세한 내용은 람다식의 예제를 참조하십시오.
Trailing Return Types_후행 반환 형식
decltype 타입 지정자와 함께 auto를 사용하여 템플릿 라이브러리를 작성할 수 있습니다. auto 및 decltype을 사용하여 반환 형식이 해당 템플릿 인수의 형식에 따라 달라지는 템플릿 함수를 선언합니다. 또는 auto 및 decltype을 사용하여 다른 함수에 대한 호출을 래핑하는 템플릿 함수를 선언한 다음 다른 함수의 반환 유형이 무엇이든 반환합니다. 자세한 내용은 decltype을 참조하십시오.
---------------------------- auto 예제 ----------------------
References and cv-qualifiers_참조 그리고 const, volatile 한정자
auto를 사용하면 참조, 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
|
#include <iostream>
using namespace std;
int main()
{
int count = 10;
int& countRef = count;
auto myAuto = countRef;
countRef = 11;
cout << count << " ";
myAuto = 12;
cout << count << endl;
const int TT = 10;
auto CT = TT;
CT = 11;
cout << count << endl;
cout << TT << " ";
cout << CT;
}
|
결과_
1
2
|
11 11
10 11
|
16줄 까지_
이전 예제에서 myAuto는 int 참조_referance형이 아니라 int 형이므로 참조 한정자가 auto로 삭제되지 않은 경우 출력은 11 11이 아니라 11 12입니다.
19줄 부터_
예제는 자체예제로서 const int를 auto로 받는 것을 실험해 본 것이다. 결과는 const int가 아닌 int 형으로 받았다. 따라서 20줄에 auto CT = TT; 절은 int CT = TT; 로 진행된다. 그래서 후에 CT값을 성공적으로 변환할 수 있는 것이었다.
단, 정말 참조형과 cv형을 못 받는 것은 아니다.
해당 예제를 다음과 같이 일부 수정을 하여 받으면 참조형과 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
|
#include <iostream>
using namespace std;
int main()
{
int count = 10;
int& countRef = count;
auto& myAuto = countRef; // 수정 부분
countRef = 11;
cout << count << " ";
myAuto = 12;
cout << count << endl;
const int TT = 10;
const auto CT = TT; // 수정 부분
CT = 11;
cout << endl;
cout << TT << " ";
cout << CT;
}
|
물론 이 코드는 20번째 const auto를 22번째에서 수정하려 하기 때문에 컴파일이 불가능하다. 만약 19줄 이후를 제외하고 이 예제를 실행한다면 이전에 얻지 못했던 결과를 얻을 수 있다.
19줄 이후 삭제 후 예제 출력_
1
|
11 12
|
성공적으로 9번줄에 선언된 countRef 참조형을 경유해 count를 바꾸었다.
중괄호 초기화_Braced initializers 를 활용한 타입 추론 (C++14)
다음 코드 예제에서는 중괄호를 사용하여 자동 변수를 초기화하는 방법을 보여줍니다. B와 C의 차이점과 A와 E의 차이점에 유의하십시오.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
#include <initializer_list>
int main()
{
// std::initializer_list<int>
auto A = { 1, 2 };
// std::initializer_list<int>
auto B = { 3 };
// int
auto C{ 4 };
// C3535: cannot deduce type for 'auto' from initializer list'
auto D = { 5, 6.7 };
// C3518 in a direct-list-initialization context the type for 'auto'
// can only be deduced from a single initializer expression
auto E{ 8, 9 };
return 0;
}
|
해당 예제를 실제 프로젝트에 옮길 경우 오류코드가 다르기에 캡쳐를 해 보았다.
뭐... 그냥 코드만 다를 뿐 내용은 같았다.
제한사항과 오류 메세지
다음 표는 auto 키워드 사용에 대한 제한사항과 컴파일러가 방출하는 해당 진단 오류 메시지를 나열합니다.
오류번호 | 설명 |
C3530 | auto 키워드는 다른 유형 지정자와 결합 될 수 없습니다. |
C3531 | auto 키워드로 선언된 심볼에는 초기화 프로그램이 있어야 합니다. |
C3532 | 유형을 선언할 때 auto 키워드를 잘못 사용했습니다. 예를 들어, 메소드 리턴 유형 또는 배열을 선언했습니다. |
C3533, C3539 | 매개 변수 또는 템플릿 인수는 auto 키워드로 선언 할 수 없습니다. |
C3535 | 함수 또는 템플릿 매개 변수는 auto 키워드로 선언 할 수 없습니다. |
C3536 | 해당 기호(Symbol-당연히 auto를 뜻함)는 초기화되기 전에 사용할 수 없습니다. 실제로 이것은 변수 자체를 초기화하는 데 사용할 수 없다는 것을 의미합니다. |
C3537 | auto 키워드로 선언된 유형으로는 캐스트(형변환)할 수 없습니다. |
C3538 | auto 키워드로 선언 된 선언자 목록의 모든 기호는 동일한 유형으로 확인되어야합니다. 자세한 내용은 선언 및 정의를 참조하십시오. |
C3540, C3541 | sizeof 및 typeid 연산자는 auto 키워드로 선언된 기호에 적용 할 수 없습니다. |
활용 예제
이 코드 조각은 auto 키워드를 사용할 수있는 몇 가지 방법을 보여줍니다.
- 다음 선언은 동일합니다.
첫 번째 문에서 변수 j는 int 유형으로 선언됩니다.
두 번째 문에서 변수 k는 초기화 표현식 (0)이 정수이기 때문에 유형 int로 추론됩니다.
1
2
|
int j = 0; // Variable j is explicitly type int.
auto k = 0; // Variable k is implicitly type int because 0 is an integer.
|
- 다음 선언은 동일하지만 두 번째 선언은 첫 번째 선언보다 간단합니다.
auto 키워드를 사용하는 가장 중요한 이유 중 하나는 단순성입니다.
1
2
|
map<int,list<string>>::iterator i = m.begin();
auto i = m.begin();
|
- 다음 코드 단락에서는 for 및 for 열 루프가 시작될 때 iter 및 elem 변수의 유형을 선언합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
// cl /EHsc /nologo /W4
#include <deque>
using namespace std;
int main()
{
deque<double> dqDoubleData(10, 0.1);
for (auto iter = dqDoubleData.begin(); iter != dqDoubleData.end(); ++iter)
{ /* ... */ }
// prefer range-for loops with the following information in mind
// (this applies to any range-for with auto, not just deque)
for (auto elem : dqDoubleData) // COPIES elements, not much better than the previous examples
{ /* ... */ }
for (auto& elem : dqDoubleData) // observes and/or modifies elements IN-PLACE
{ /* ... */ }
for (const auto& elem : dqDoubleData) // observes elements IN-PLACE
{ /* ... */ }
}
|
각각 원하는 &형 const & 형을 받을 수 있으며, 배열을 정상적으로 돈다. 더하여 이전에 int을 해야한다는 조건 같은 것도 없으며 단독으로도 성공적으로 이터레이터를 추론하여 준다. (실험 다 해봄 - 디버그 모드로 진입만 해도 아는 부분...)
- 다음 코드 단편은 new 연산자와 포인터 선언을 사용하여 포인터를 선언합니다.
1
2
|
double x = 12.34;
auto *y = new auto(x), **z = new auto(&x);
|
- 다음 코드 단편은 각 선언문에 여러 기호를 선언합니다.
각 명령문의 모든 기호는 동일한 유형으로 해석됩니다.
1
2
3
4
|
auto x = 1, *y = &x, **z = &y; // Resolves to int.
auto a(2.01), *b (&a); // Resolves to double.
auto c = 'a', *d(&c); // Resolves to char.
auto m = 1, &n = m; // Resolves to int.
|
- 이 코드 조각은 조건 연산자 (? :)를 사용하여 변수 x를 200의 값을 갖는 정수로 선언합니다.
1
2
|
int v1 = 100, v2 = 200;
auto x = v1 > v2 ? v1 : v2;
|
해당 x는 int형이다.
- 다음 코드 단편은 변수 x를 int 유형으로, 변수 y를 유형 const int에 대한 참조로, 변수 fp를 유형 int를 리턴하는 함수에 대한 포인터로 초기화합니다.
1
2
3
4
5
6
7
8
9
10
|
int f(int x) { return x; }
int main()
{
auto x = f(0);
const auto & y = f(1);
int (*p)(int x);
p = f;
auto fp = p;
//...
}
|
참고
auto Keyword
Keywords
/Zc:auto (Deduce Variable Type)
sizeof Operator
typeid
operator new
Declarations and Definitions
Examples of Lambda Expressions
Initializers
decltype
'C++' 카테고리의 다른 글
Decltype (C++) (declare type) (0) | 2019.12.25 |
---|---|
volatile (0) | 2019.12.25 |
저장소 클래스 - Storage classes (static, extern, thread_local, register) (0) | 2019.12.25 |
Timer - 시간 측정기 (0) | 2019.12.25 |
const*, constexpr** (0) | 2019.12.25 |