매크로 사용을 선호하는 곳과 constexpr을 선호해야하는 곳 입니까? 기본적으로 동일하지 않습니까?
#define MAX_HEIGHT 720
vs
constexpr unsigned int max_height = 720;
답변
기본적으로 동일하지 않습니까?
아뇨. 절대 아닙니다. 근처에도 안.
그렇다 사실에서 매크로는이다 int
당신은 constexpr unsigned
입니다 unsigned
만이 중요한 차이점 및 매크로가, 하나의 장점은.
범위
매크로는 전처리기에 의해 정의되며 발생할 때마다 코드로 대체됩니다. 전처리 기는 멍청 하고 C ++ 구문이나 의미를 이해하지 못합니다. 매크로는 네임 스페이스, 클래스 또는 함수 블록과 같은 범위를 무시하므로 소스 파일에서 다른 이름을 사용할 수 없습니다. 적절한 C ++ 변수로 정의 된 상수에는 해당되지 않습니다.
#define MAX_HEIGHT 720
constexpr int max_height = 720;
class Window {
// ...
int max_height;
};
max_height
클래스 멤버이므로 범위가 다르고 네임 스페이스 범위의 변수 와 구별되므로 멤버 변수를 호출 하는 것이 좋습니다. MAX_HEIGHT
멤버 의 이름을 다시 사용하려고 하면 전처리 기가 컴파일하지 않는 말도 안되는 이름 으로 변경합니다.
class Window {
// ...
int 720;
};
그렇기 때문에 매크로 UGLY_SHOUTY_NAMES
가 눈에 잘 띄 도록 매크로를 제공 해야하며 충돌을 피하기 위해 이름을 지정할 때주의해야합니다. 불필요하게 매크로를 사용하지 않으면 그것에 대해 걱정할 필요가 없습니다 (그리고 읽을 필요도 없습니다.SHOUTY_NAMES
).
함수 내부에 상수를 원하면 전처리 기가 함수가 무엇인지 또는 그 안에 있다는 것이 무엇을 의미하는지 알지 못하기 때문에 매크로로이를 수행 할 수 없습니다. 매크로를 파일의 특정 부분으로 만 제한하려면 #undef
다시 필요 합니다.
int limit(int height) {
#define MAX_HEIGHT 720
return std::max(height, MAX_HEIGHT);
#undef MAX_HEIGHT
}
훨씬 더 현명한 것과 비교하십시오.
int limit(int height) {
constexpr int max_height = 720;
return std::max(height, max_height);
}
왜 매크로를 선호합니까?
실제 메모리 위치
constexpr 변수 는 변수 이므로 실제로 프로그램에 존재하며 주소를 가져오고 참조를 바인딩하는 것과 같은 일반적인 C ++ 작업을 수행 할 수 있습니다.
이 코드에는 정의되지 않은 동작이 있습니다.
#define MAX_HEIGHT 720
int limit(int height) {
const int& h = std::max(height, MAX_HEIGHT);
// ...
return h;
}
문제는 MAX_HEIGHT
변수가 아니기 때문에 std::max
임시 호출을 int
컴파일러에 의해 생성해야한다는 것입니다. 에서 반환되는 std::max
참조는 해당 문이 끝난 후에 존재하지 않는 임시를 참조 할 수 있으므로return h
잘못된 메모리에 액세스합니다.
그 문제는 사라지지 않는 고정 된 메모리 위치를 가지고 있기 때문에 적절한 변수로는 존재하지 않습니다.
int limit(int height) {
constexpr int max_height = 720;
const int& h = std::max(height, max_height);
// ...
return h;
}
(실제로는 선언 int h
하지 않을 수 const int& h
있지만 더 미묘한 상황에서 문제가 발생할 수 있습니다.)
전 처리기 조건
매크로를 선호하는 유일한 시간은 #if
조건에서 사용하기 위해 전처리 기가 해당 값을 이해해야 할 때입니다.
#define MAX_HEIGHT 720
#if MAX_HEIGHT < 256
using height_type = unsigned char;
#else
using height_type = unsigned int;
#endif
전처리 기가 이름으로 변수를 참조하는 방법을 이해하지 못하기 때문에 여기서 변수를 사용할 수 없습니다. 매크로 확장 및 #
(like #include
and #define
and and)로 시작하는 지시문과 같은 기본적인 매우 기본적인 것만 이해합니다.#if
)로 합니다.
전처리 기가 이해할 수 있는 상수를 원하면 전처리기를 사용하여 정의해야합니다. 일반 C ++ 코드에 대한 상수가 필요한 경우 일반 C ++ 코드를 사용하십시오.
위의 예는 전 처리기 조건을 보여주기위한 것이지만 해당 코드에서도 전 처리기 사용을 피할 수 있습니다.
using height_type = std::conditional_t<max_height < 256, unsigned char, unsigned int>;
답변
일반적으로 말해서 constexpr
가능할 때마다 사용 하고 다른 해결책이 불가능한 경우에만 매크로 를 사용해야 합니다.
이론적 해석:
매크로는 코드에서 간단한 대체이며 이러한 이유로 종종 충돌이 발생합니다 (예 : windows.h max
매크로 대 std::max
). 또한 작동하는 매크로는 다른 방식으로 쉽게 사용되어 이상한 컴파일 오류를 유발할 수 있습니다. (예 : Q_PROPERTY
구조 부재에 사용)
이러한 모든 불확실성으로 인해 일반적으로 gotos를 피하는 것처럼 매크로를 피하는 것이 좋은 코드 스타일입니다.
constexpr
의미 론적으로 정의되므로 일반적으로 훨씬 적은 문제를 생성합니다.
답변
Jonathon Wakely의 훌륭한 답변 입니다. 또한 매크로 사용을 고려하기 전에 차이점 과 차이점에 대한 jogojapan의 답변 을 살펴 보는 것이 좋습니다.const
constexpr
매크로는 멍청하지만 좋은 의미입니다. 표면적으로는 요즘에는 코드의 매우 특정한 부분이 “정의 된”특정 빌드 매개 변수가있는 경우에만 컴파일되기를 원할 때 빌드 보조 도구입니다. 일반적으로, 수단이 더 좋은 방법은 매크로 이름, 또는 복용 모든 것을, 이제 그것을 부르 자 Trigger
, 및 추가 상황이 좋아 /D:Trigger
, -DTrigger
사용되는 빌드 도구 등.
매크로에 대한 다양한 용도가 있지만 다음은 나쁘거나 오래된 관행이 아닌 가장 자주 보는 두 가지입니다.
- 하드웨어 및 플랫폼 별 코드 섹션
- 늘어난 상세 빌드
따라서 OP의 경우 constexpr
또는 a 로 int를 정의하는 동일한 목표를 달성 MACRO
할 수 있지만 현대적인 규칙을 사용할 때 둘이 겹칠 가능성은 없습니다. 다음은 아직 단계적으로 제거되지 않은 일반적인 매크로 사용입니다.
#if defined VERBOSE || defined DEBUG || defined MSG_ALL
// Verbose message-handling code here
#endif
매크로 사용의 또 다른 예로, 출시 예정인 하드웨어가 있거나 다른 하드웨어가 필요로하지 않는 까다로운 해결 방법이있는 특정 세대가 있다고 가정 해 보겠습니다. 이 매크로를 GEN_3_HW
.
#if defined GEN_3_HW && defined _WIN64
// Windows-only special handling for 64-bit upcoming hardware
#elif defined GEN_3_HW && defined __APPLE__
// Special handling for macs on the new hardware
#elif !defined _WIN32 && !defined __linux__ && !defined __APPLE__ && !defined __ANDROID__ && !defined __unix__ && !defined __arm__
// Greetings, Outlander! ;)
#else
// Generic handling
#endif