매크로 사용을 선호하는 곳과 constexpr을 선호해야하는 곳 입니까? 기본적으로 동일하지 않습니까?
#define MAX_HEIGHT 720
vs
constexpr unsigned int max_height = 720;
매크로 사용을 선호하는 곳과 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
변수 필요없는 촬영의 어드레스 (포인터 / 참조)까지 메모리를 점유; 그렇지 않으면 완전히 최적화 될 수 있습니다 (그리고이를 보장하는 Standardese가있을 수 있다고 생각합니다). 그럼에도 불구하고 스토리지가 필요하지 않은 enum
사소한 constexpr
것이 일부를 차지할 것이라는 잘못된 생각에서 사람들이 오래되고 열등한 ' 핵'을 계속 사용하지 않도록 이것을 강조하고 싶습니다 .
int height
매크로와 마찬가지로 문제가 될 것입니다. 그 범위는 함수에 묶여 있고 본질적으로 일시적이기 때문입니다. 3. 위의 주석 "const int & h는 임시 수명을 연장합니다"가 맞습니다.
limit
문제는의 반환 값입니다 std::max
. 2. 예, 그것이 참조를 반환하지 않는 이유입니다. 3. 잘못되었습니다. 위의 coliru 링크를 참조하십시오.
const int& h = max(x, y);
있고 max
그 값으로 반환 하면 반환 값의 수명이 연장됩니다. 반환 유형이 아니라 const int&
바인딩되어 있습니다. 내가 쓴 내용이 맞습니다.
일반적으로 말해서 constexpr
가능할 때마다 사용 하고 다른 해결책이 불가능한 경우에만 매크로 를 사용해야 합니다.
매크로는 코드에서 간단한 대체이며 이러한 이유로 종종 충돌이 발생합니다 (예 : windows.h max
매크로 대 std::max
). 또한 작동하는 매크로는 다른 방식으로 쉽게 사용되어 이상한 컴파일 오류를 유발할 수 있습니다. (예 : Q_PROPERTY
구조 부재에 사용)
이러한 모든 불확실성으로 인해 일반적으로 gotos를 피하는 것처럼 매크로를 피하는 것이 좋은 코드 스타일입니다.
constexpr
의미 론적으로 정의되므로 일반적으로 훨씬 적은 문제를 생성합니다.
#if
즉, 전처리 기가 실제로 유용한 것들을 사용하는 조건부 컴파일 . 상수 를 사용하는 전 처리기 조건에서 사용되기 때문에 상수 가 매크로 여야 하는 경우가 아니라면 상수를 정의하는 것은 전처리 기가 유용한 요소 중 하나가 아닙니다 #if
. 상수가 일반 C ++ 코드 (전 처리기 지시문이 아님)에서 사용되는 경우 전 처리기 매크로가 아닌 일반 C ++ 변수를 사용하십시오.
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