Constexpr 대 매크로


92

매크로 사용을 선호하는 곳과 constexpr을 선호해야하는 곳 입니까? 기본적으로 동일하지 않습니까?

#define MAX_HEIGHT 720

vs

constexpr unsigned int max_height = 720;

4
AFAIK의 constexpr 더 많은 형태의 안전성을 제공합니다
코드 - 견습생에게

13
쉬움 : 항상 constexr.
n. '대명사'm.

질문의 일부 답변 할 수 있음 stackoverflow.com/q/4748083/540286
Ortwin Angermeier

답변:


146

기본적으로 동일하지 않습니까?

아뇨. 절대 아닙니다. 근처에도 안.

그렇다 사실에서 매크로는이다 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 #includeand #defineand and)로 시작하는 지시문과 같은 기본적인 매우 기본적인 것만 이해합니다.#if )로 합니다.

전처리 기가 이해할 수 있는 상수를 원하면 전처리기를 사용하여 정의해야합니다. 일반 C ++ 코드에 대한 상수가 필요한 경우 일반 C ++ 코드를 사용하십시오.

위의 예는 전 처리기 조건을 보여주기위한 것이지만 해당 코드에서도 전 처리기 사용을 피할 수 있습니다.

using height_type = std::conditional_t<max_height < 256, unsigned char, unsigned int>;

3
constexpr변수 필요없는 촬영의 어드레스 (포인터 / 참조)까지 메모리를 점유; 그렇지 않으면 완전히 최적화 될 수 있습니다 (그리고이를 보장하는 Standardese가있을 수 있다고 생각합니다). 그럼에도 불구하고 스토리지가 필요하지 않은 enum사소한 constexpr것이 일부를 차지할 것이라는 잘못된 생각에서 사람들이 오래되고 열등한 ' 핵'을 계속 사용하지 않도록 이것을 강조하고 싶습니다 .
underscore_d

3
"실제 메모리 위치"섹션이 잘못되었습니다. 1. 값 (int)으로 반환하므로 복사본이 만들어지고 임시는 문제가되지 않습니다. 2. 참조 (int &)로 반환했다면 int height매크로와 마찬가지로 문제가 될 것입니다. 그 범위는 함수에 묶여 있고 본질적으로 일시적이기 때문입니다. 3. 위의 주석 "const int & h는 임시 수명을 연장합니다"가 맞습니다.
PoweredByRice

4
@underscore_d true이지만 인수를 변경하지는 않습니다. 변수를 사용하지 않는 한이 변수는 저장 공간이 필요하지 않습니다. 요점은 스토리지가있는 실제 변수가 필요할 때 constexpr 변수가 올바른 일을한다는 것입니다.
Jonathan Wakely

1
@PoweredByRice 1. 문제는의 반환 값과 관련이 없으며 limit문제는의 반환 값입니다 std::max. 2. 예, 그것이 참조를 반환하지 않는 이유입니다. 3. 잘못되었습니다. 위의 coliru 링크를 참조하십시오.
Jonathan Wakely

3
@PoweredByRice 한숨, C ++이 어떻게 작동하는지 설명 할 필요가 없습니다. 값을 가지고 const int& h = max(x, y);있고 max그 값으로 반환 하면 반환 값의 수명이 연장됩니다. 반환 유형이 아니라 const int&바인딩되어 있습니다. 내가 쓴 내용이 맞습니다.
Jonathan Wakely

11

일반적으로 말해서 constexpr가능할 때마다 사용 하고 다른 해결책이 불가능한 경우에만 매크로 를 사용해야 합니다.

이론적 해석:

매크로는 코드에서 간단한 대체이며 이러한 이유로 종종 충돌이 발생합니다 (예 : windows.h max 매크로 대 std::max). 또한 작동하는 매크로는 다른 방식으로 쉽게 사용되어 이상한 컴파일 오류를 유발할 수 있습니다. (예 : Q_PROPERTY구조 부재에 사용)

이러한 모든 불확실성으로 인해 일반적으로 gotos를 피하는 것처럼 매크로를 피하는 것이 좋은 코드 스타일입니다.

constexpr 의미 론적으로 정의되므로 일반적으로 훨씬 적은 문제를 생성합니다.


1
어떤 경우에 매크로 사용이 불가피합니까?
Tom Dorone 2017

3
#if즉, 전처리 기가 실제로 유용한 것들을 사용하는 조건부 컴파일 . 상수 사용하는 전 처리기 조건에서 사용되기 때문에 상수 매크로 여야 하는 경우가 아니라면 상수를 정의하는 것은 전처리 기가 유용한 요소 중 하나가 아닙니다 #if. 상수가 일반 C ++ 코드 (전 처리기 지시문이 아님)에서 사용되는 경우 전 처리기 매크로가 아닌 일반 C ++ 변수를 사용하십시오.
Jonathan Wakely

가변 매크로를 사용하는 것을 제외하고는 대부분 컴파일러 스위치에 매크로를 사용하지만 실제 코드 문을 처리하는 현재 매크로 문 (예 : 조건부, 문자열 리터럴 스위치)을 constexpr로 바꾸는 것이 좋습니다.

나는 컴파일러 스위치도 좋은 생각이 아니라고 말할 것입니다. 그러나 나는 특히 크로스 플랫폼 또는 임베디드 코드를 다루는 데 때때로 (매크로) 필요하다는 것을 완전히 이해합니다. 귀하의 질문에 답하기 위해 : 이미 전처리기를 다루고 있다면 매크로를 사용하여 전처리 기가 무엇이며 컴파일 시간이 무엇인지 명확하고 직관적으로 유지합니다. 나는 또한 많은 주석을 달고 가능한 한 짧고 지역적으로 사용하도록 제안합니다 (매크로가 퍼지는 것을 피하거나 100 줄 #if). 아마도 예외는 잘 알려진 전형적인 #ifndef 가드 (#pragma once의 표준) 일 것입니다.
아드리안 MAIRE

3

Jonathon Wakely의 훌륭한 답변 입니다. 또한 매크로 사용을 고려하기 전에 차이점 과 차이점에 대한 jogojapan의 답변 을 살펴 보는 것이 좋습니다.constconstexpr

매크로는 멍청하지만 좋은 의미입니다. 표면적으로는 요즘에는 코드의 매우 특정한 부분이 "정의 된"특정 빌드 매개 변수가있는 경우에만 컴파일되기를 원할 때 빌드 보조 도구입니다. 일반적으로, 수단이 더 좋은 방법은 매크로 이름, 또는 복용 모든 것을, 이제 그것을 부르 자 Trigger, 및 추가 상황이 좋아 /D:Trigger, -DTrigger사용되는 빌드 도구 등.

매크로에 대한 다양한 용도가 있지만 다음은 나쁘거나 오래된 관행이 아닌 가장 자주 보는 두 가지입니다.

  1. 하드웨어 및 플랫폼 별 코드 섹션
  2. 늘어난 상세 빌드

따라서 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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.