1.1. #define (매크로)
- 전처리기 지시자로, 컴파일러가 소스 코드를 컴파일하기 전에 텍스트를 치환하는 역할을 합니다.
- 예를 들어, 이런 식으로 선언하면, 코드를 전처리 단계에서 MAX는 100으로, x는 first로 치환합니다.
-
#define MAX 100
#define x first
#define y second
- 장점: 간단한 치환, 빠른 코딩.
- 단점:
- 전처리 단계에서 단순 텍스트 치환이므로, 디버깅 시 예상치 못한 오류를 유발할 수 있음.
- 스코프 개념이 없어(매크로라서), 범위 외 혼동이 일어날 수 있음.
1.2. typedef
- 타입에 새로운 이름(별칭)을 부여합니다.
- 예를 들어,
typedef long long ll;
typedef pair<int,int> pii;
이라고 하면 long long을 ll로, pair<int,int>를 pii라는 이름으로 대체할 수 있습니다.
- C++에서는 typedef 보다는 뒤에서 설명할 using을 좀 더 권장하는 편입니다.
그러나 둘 다 타입을 대상으로 한다는 점에서는 동일한 목적을 가집니다.
1.3. using
- C++11부터 지원하는 타입 별칭 문법입니다.
- typedef와 비슷하지만, 템플릿 문법과 결합하기가 더 좋고 가독성도 뛰어납니다.
- 예를 들어,
using ll = long long;
using pii = pair<int,int>;
이라고 선언하면 ll, pii를 쓸 수 있게 됩니다.
- 주의: using 역시 **“타입”**에 대한 별칭만 만들 수 있습니다.
멤버 변수(예: pair<int,int>의 first, second)에 대해 직접 별칭을 만들 수는 없습니다.
2. using x = first; using y = second;가 안 되는 이유
- pair<int,int>::first/second는 타입이 아니라 멤버 변수(정확히는 public 멤버 변수)입니다.
- using은 타입에만 사용 가능하기 때문에, 다음처럼 멤버 변수 이름에 using을 적용할 수 없습니다.
// 오류 예시
using x = first; // ❌ first는 타입이 아니다!
using y = second; // ❌ second도 타입이 아니다!
3. 이 경우(멤버 변수에 대한 별칭) 어떻게 고쳐야 하는가?
3.1. 매크로(#define)로 치환하기
- 전처리기에서 간단한 텍스트 치환으로 해결 가능합니다.
- 즉, 아래와 같이 매크로로 x, y를 정의하면 됩니다.
#define x first
#define y second
int main() {
pair<int,int> p = {10, 20};
cout << p.x << " " << p.y << endl; // 10 20
return 0;
}
- 이 방법이 경쟁 프로그래밍 등에서 자주 사용되며, 멤버 변수 이름을 짧게 쓰고 싶을 때 편리합니다.
단, 매크로의 단점(디버깅 어려움, 스코프 제한 없음 등)을 감안해야 합니다.
3.2. 구조체(클래스)로 직접 만들어 쓰기
- pair를 직접 쓰지 않고, 구조체(또는 클래스)로 감싸서 x, y 멤버로 두는 방법도 가능합니다.
- 예를 들어,
struct Point {
int x, y;
};
int main() {
Point p{10, 20};
cout << p.x << " " << p.y << endl; // 10 20
return 0;
}
- 이 경우에는 매크로 사용 없이 x, y를 멤버로 가지고 있어서 직관적이지만, pair의 특성을 잃게 됩니다(별도의 구조체니 당연하지만).
4. 언제 using으로 가능한가?
// 1) pair<int, int> 자체에 별칭을 주는 것은 가능:
using pii = pair<int, int>;
// 2) 템플릿 타입 별칭:
template<typename T>
using vec = vector<T>;
// 3) 함수 포인터 타입, 특정 함수 시그니처의 별칭 등
using func_t = int(*)(int);
- 즉, using 키워드로 멤버 변수를 바로 ‘바꿔치기’하는 것은 안 되고, 타입(클래스, 템플릿, 함수 포인터, 기타)만 가능합니다.