[c++] C ++ 11에서 언제 constexpr 기능을 사용해야합니까?

“항상 5를 반환하는 함수”를 갖는 것이 “함수 호출”의 의미를 깨뜨 리거나 희석시키는 것 같습니다. 이유가 있거나이 기능이 필요하거나 C ++ 11에 있지 않아야합니다. 왜 거기에 있습니까?

// preprocessor.
#define MEANING_OF_LIFE 42

// constants:
const int MeaningOfLife = 42;

// constexpr-function:
constexpr int MeaningOfLife () { return 42; }

리터럴 값을 반환하는 함수를 작성하고 코드 검토를 수행하면 누군가가 반환 5를 작성하는 대신 상수 값을 선언해야한다고 말할 것입니다.



답변

좀 더 복잡한 일을한다고 가정 해보십시오.

constexpr int MeaningOfLife ( int a, int b ) { return a * b; }

const int meaningOfLife = MeaningOfLife( 6, 7 );

이제 상수를 숫자로 설정하는 것보다 가독성을 높이고 약간 더 복잡한 처리를 허용하면서 상수로 평가할 수있는 것이 있습니다.

기본적으로 유지 관리에 도움이됩니다. 가지고 max( a, b )예를 들면 :

template< typename Type > constexpr Type max( Type a, Type b ) { return a < b ? b : a; }

매우 간단한 선택이지만 max상수 값으로 호출하면 런타임이 아닌 컴파일 타임에 명시 적으로 계산됩니다.

또 다른 좋은 예는 DegreesToRadians함수입니다. 누구나 라디안보다 읽기 쉬운 정도를 찾습니다. 180 도가 라디안 인 것을 알 수 있지만 다음과 같이 훨씬 명확하게 작성됩니다.

const float oneeighty = DegreesToRadians( 180.0f );

여기에 좋은 정보가 많이 있습니다 :

http://en.cppreference.com/w/cpp/language/constexpr


답변

소개

constexpr상수 표현 이 필요한 상황에서 무언가를 평가할 수 있음을 구현에 알리는 방법으로 소개되지 않았습니다 . 적합한 구현은 C ++ 11 이전에 이것을 증명할 수있었습니다.

구현이 증명할 수없는 것은 특정 코드 의 의도 입니다.

  • 개발자가이 엔티티로 표현하고자하는 것은 무엇입니까?
  • 코드 가 작동하기 때문에 상수 표현식 에서 코드를 맹목적으로 허용해야합니까 ?

세상이 없으면 constexpr무엇이 될까요?

라이브러리를 개발 중이고 interval의 모든 정수의 합을 계산할 수 있기를 원한다고 가정 해 봅시다 (0,N].

int f (int n) {
  return n > 0 ? n + f (n-1) : n;
}

의도 부족

컴파일러는 전달 된 인수가 변환 중에 알려진 경우 상수 표현식 에서 위 함수를 호출 할 수 있음을 쉽게 증명할 수 있습니다 . 그러나 당신은 이것을 의도로 선언하지 않았습니다-그것은 단지 사건이되었습니다.

이제 다른 누군가가 와서 함수를 읽고 컴파일러와 동일한 분석을 수행합니다. ” 아,이 함수는 상수 표현으로 사용할 수 있습니다!” 다음 코드를 작성합니다.

T arr[f(10)]; // freakin' magic

최적화

당신은, int로서 “최고” 라이브러리 개발자, 즉 결정 f호출되는 경우 결과를 캐시한다 누가 같은 값 집합을 계속해서 계산하고 싶습니까?

int func (int n) {
  static std::map<int, int> _cached;

  if (_cached.find (n) == _cached.end ())
    _cached[n] = n > 0 ? n + func (n-1) : n;

  return _cached[n];
}

결과

바보 같은 최적화를 도입함으로써 상수 표현 이 필요한 상황에서 발생한 함수의 모든 사용을 중단했습니다 .

당신은 함수가에서 사용할 것을 약속 결코 일정한 표현 , 그리고없이 constexpr같은 약속을 제공하는 방법이 없습니다.


왜 우리는 필요 constexpr합니까?

constexpr 의 주요 사용법은 intent 선언 입니다.

엔터티가로 표시되지 않은 경우 constexpr 상수 표현식에 . 그리고 그것이더라도, 우리는 컴파일러를 사용하여 그러한 상황을 진단합니다 (우리의 의도를 무시하기 때문에).


답변

취하기 std::numeric_limits<T>::max(): 어떤 이유로 든 이것은 방법입니다. constexpr여기에 도움이 될 것입니다.

또 다른 예 : std::array다른 배열만큼 큰 C 배열 (또는 ) 을 선언하려고합니다 . 현재이 작업을 수행하는 방법은 다음과 같습니다.

int x[10];
int y[sizeof x / sizeof x[0]];

그러나 다음과 같이 쓸 수있는 것이 더 좋지 않을 것입니다.

int y[size_of(x)];

덕분에 다음 constexpr을 수행 할 수 있습니다.

template <typename T, size_t N>
constexpr size_t size_of(T (&)[N]) {
    return N;
}


답변

constexpr함수는 정말 좋고 c ++에 크게 추가되었습니다. 그러나 해결하는 대부분의 문제는 매크로를 사용하여 우아하게 해결할 수 있습니다.

그러나의 사용 중 하나 constexpr에는 C ++ 03과 동등한 유형의 상수가 없습니다.

// This is bad for obvious reasons.
#define ONE 1;

// This works most of the time but isn't fully typed.
enum { TWO = 2 };

// This doesn't compile
enum { pi = 3.1415f };

// This is a file local lvalue masquerading as a global
// rvalue.  It works most of the time.  But May subtly break
// with static initialization order issues, eg pi = 0 for some files.
static const float pi = 3.1415f;

// This is a true constant rvalue
constexpr float pi = 3.1415f;

// Haven't you always wanted to do this?
// constexpr std::string awesome = "oh yeah!!!";
// UPDATE: sadly std::string lacks a constexpr ctor

struct A
{
   static const int four = 4;
   static const int five = 5;
   constexpr int six = 6;
};

int main()
{
   &A::four; // linker error
   &A::six; // compiler error

   // EXTREMELY subtle linker error
   int i = rand()? A::four: A::five;
   // It not safe use static const class variables with the ternary operator!
}

//Adding this to any cpp file would fix the linker error.
//int A::four;
//int A::six;


답변

내가 읽은 것에서 constexpr의 필요성은 메타 프로그래밍의 문제에서 비롯됩니다. 특성 클래스는 함수로 표현되는 상수를 가질 수 있습니다. numeric_limits :: max (). constexpr을 사용하면 이러한 유형의 함수를 메타 프로그래밍 또는 배열 경계 등으로 사용할 수 있습니다.

내 머리 꼭대기의 또 다른 예는 클래스 인터페이스의 경우 파생 된 유형이 일부 작업에 대해 자체 상수를 정의하기를 원할 수 있습니다.

편집하다:

SO를 둘러 본 후 다른 사람들이 constexprs로 가능한 것들에 대한 몇 가지 를 생각해 냈습니다 .


답변

“Going Native 2012″에서 Stroustrup의 연설에서 :

template<int M, int K, int S> struct Unit { // a unit in the MKS system
       enum { m=M, kg=K, s=S };
};

template<typename Unit> // a magnitude with a unit 
struct Value {
       double val;   // the magnitude 
       explicit Value(double d) : val(d) {} // construct a Value from a double 
};

using Speed = Value<Unit<1,0,-1>>;  // meters/second type
using Acceleration = Value<Unit<1,0,-2>>;  // meters/second/second type
using Second = Unit<0,0,1>;  // unit: sec
using Second2 = Unit<0,0,2>; // unit: second*second 

constexpr Value<Second> operator"" s(long double d)
   // a f-p literal suffixed by ‘s’
{
  return Value<Second> (d);
}

constexpr Value<Second2> operator"" s2(long double d)
  // a f-p literal  suffixed by ‘s2’ 
{
  return Value<Second2> (d);
}

Speed sp1 = 100m/9.8s; // very fast for a human 
Speed sp2 = 100m/9.8s2; // error (m/s2 is acceleration)  
Speed sp3 = 100/9.8s; // error (speed is m/s and 100 has no unit) 
Acceleration acc = sp1/0.5s; // too fast for a human


답변

아직 언급되지 않은 또 다른 용도는 constexpr생성자입니다. 이를 통해 런타임 중에 초기화 할 필요가없는 컴파일 시간 상수를 작성할 수 있습니다.

const std::complex<double> meaning_of_imagination(0, 42); 

사용자 정의 리터럴과 쌍을 이루면 리터럴 사용자 정의 클래스를 완벽하게 지원합니다.

3.14D + 42_i;