컴파일 타임에 C 문자열의 길이를 계산합니다. 이것은 정말로 constexpr입니까?


94

컴파일 타임에 문자열 리터럴의 길이를 계산하려고합니다. 이를 위해 다음 코드를 사용하고 있습니다.

#include <cstdio>

int constexpr length(const char* str)
{
    return *str ? 1 + length(str + 1) : 0;
}

int main()
{
    printf("%d %d", length("abcd"), length("abcdefgh"));
}

모든 것이 예상대로 작동하고 프로그램은 4와 8을 인쇄합니다. clang에 의해 생성 된 어셈블리 코드는 결과가 컴파일 타임에 계산됨을 보여줍니다.

0x100000f5e:  leaq   0x35(%rip), %rdi          ; "%d %d"
0x100000f65:  movl   $0x4, %esi
0x100000f6a:  movl   $0x8, %edx
0x100000f6f:  xorl   %eax, %eax
0x100000f71:  callq  0x100000f7a               ; symbol stub for: printf

내 질문 : length함수가 컴파일 시간에 평가된다는 것이 표준에 의해 보장 됩니까?

이것이 사실이라면 컴파일 시간 문자열 리터럴 계산의 문이 방금 열렸습니다 ... 예를 들어 컴파일 시간에 해시를 계산할 수 있습니다.


3
매개 변수가 상수 표현식이면 반드시 그래야합니다.
chris

1
@chris 상수 표현식 이 필요하지 않은 컨텍스트에서 사용될 때 상수 표현식 될 수있는 것을 컴파일 타임에 평가 해야 한다는 보장이 있습니까?
TC

12
BTW, 포함 <cstdio>및 호출 ::printf은 휴대용이 아닙니다. 표준 <cstdio>std::printf.
Ben Voigt 2014 년

1
@BenVoigt Ok, 지적 해 주셔서 감사합니다 :) 처음에는 std :: cout을 사용했지만 생성 된 코드는 실제 값을 찾기에는 꽤 컸습니다.)
Mircea Ispas

3
@Felics 최적화를 다루는 질문에 답할 때 종종 godbolt를 사용 하고 사용 printf하면 처리 할 코드가 훨씬 줄어들 수 있습니다.
Shafik Yaghmour 2014 년

답변:


76

상수 표현식은 컴파일 타임에 평가된다는 보장이 없으며, C ++ 표준 섹션 5.19 상수 표현식 초안 에서 비표준적인 인용문 만 있습니다.

[...]> [참고 : 변환 중에 상수 표현식을 평가할 수 있습니다 .—end note]

당신은에 결과를 할당 할 수 constexpr있는지가 컴파일시에 평가 될 변수, 우리는에서이 볼 수 11 참조 ++ 비얀 스트로브 스트 룹의 C (라고 강조 광산 ) :

컴파일 타임에 표현식을 평가할 수있을뿐만 아니라 컴파일 타임에 표현식을 평가 하도록 요구할 수 있습니다. 변수 정의 앞의 constexpr은 그렇게합니다 (그리고 const를 의미합니다) :

예를 들면 :

constexpr int len1 = length("abcd") ;

Bjarne Stroustrup은이 isocpp 블로그 항목 에서 컴파일 시간 평가를 보장 할 수있는시기에 대한 요약을 제공하며 다음 과 같이 말합니다.

[...] 정답은-Herb에서 언급했듯이-표준에 ​​따라 constexpr 함수는 상수 표현식으로 사용되지 않는 한 컴파일러 시간 또는 런타임에 평가 될 수 있으며,이 경우 컴파일시 평가되어야합니다. -시각. 컴파일 타임 평가를 보장하려면 상수 표현식이 필요한 곳에 사용하거나 (예 : 배열 바인딩 또는 케이스 레이블로)이를 사용하여 constexpr을 초기화해야합니다. 나는 자존심이 강한 컴파일러가 내가 원래 말했던 최적화 기회를 놓치지 않기를 바란다 : "모든 인수가 상수 표현식이라면 constexpr 함수는 컴파일 타임에 평가된다."

따라서 이것은 컴파일 타임에 평가되어야하는 두 가지 경우에 대해 설명합니다.

  1. 상수 표현식이 필요한 곳에 사용하십시오. 이것은 shall be ... converted constant expression또는 구문 shall be ... constant expression이 사용되는 초안 표준 ( 예 : 배열 바인드)의 어느 곳에 나있는 것처럼 보입니다 .
  2. constexpr위에서 설명한대로 초기화하는 데 사용하십시오 .

4
즉, 원칙적 으로 컴파일러는 내부 또는 연결되지 않은 개체를 볼 수 있으며 constexpr int x = 5;컴파일 타임에 값이 필요하지 않다는 것을 관찰하고 (템플릿 매개 변수로 사용되지 않는다고 가정 할 때) 실제로 방출합니다. 1 및 4 덧셈 연산의 5 개의 즉치 값을 사용하여 런타임에 초기 값을 계산하는 코드입니다. 보다 현실적인 예 : 컴파일러가 재귀 제한에 도달하고 런타임까지 계산을 연기 할 수 있습니다. 컴파일러가 실제로 값을 사용하도록 강제하는 작업을 수행하지 않는 한 "컴파일 시간에 평가되도록 보장"은 QOI 문제입니다.
Steve Jessop

@SteveJessop Bjarne은 번역에서 평가 되는 상수 표현 수단 으로 사용되는 초안 표준에서 찾을 수있는 아날로그가없는 개념을 사용하는 것 같습니다 . 그래서 표준이 그가 말하는 것을 명시 적으로 언급하지 않는 것처럼 보일 것이므로 나는 당신과 동의하는 경향이 있습니다. Bjarne과 Herb 모두 이것에 동의하는 것처럼 보이지만, 이는 단지 불명확 함을 나타낼 수 있습니다.
Shafik Yaghmour

2
나는 그들이 내가 가정 한 표준을 따르지만 의도적으로 방해가되는 컴파일러와는 반대로 "자기 존중하는 컴파일러"만을 고려하고 있다고 생각한다. 실제로 어떤 표준에 대해 추론하는 수단으로 유용 보장 ;-), 그리고 많은 다른
스티브 Jessop

3
@SteveJessop 악명 높은 (그리고 불행히도 존재하지 않는) Hell ++와 같은 의도적으로 방해가되는 컴파일러. 그러한 것은 실제로 적합성 / 이동성을 테스트하는 데 유용 할 것입니다.
문헌 : Angew는 더 이상 자랑 SO의없는

as-if 규칙에 따라 값을 컴파일 시간 상수로 사용하는 것만으로는 충분하지 않습니다. 컴파일러는 소스의 복사본을 자유롭게 제공하고 런타임에 다시 컴파일하거나 루티 계산을 수행하여 변수, 또는 constexpr순전히 악에서 계산을 무의미하게 다시 실행하십시오 . 주어진 소스 라인에서 캐릭터 당 1 초를 기다릴 수도 있고, 주어진 소스 라인을 가져 와서 체스 포지션을 시드하는 데 사용하고 양쪽을 플레이하여 누가 이겼는지 결정할 수도 있습니다.
Yakk-Adam Nevraumont 2014 년

27

constexpr함수 호출 이 핵심 상수 표현식을 생성 하는지 아니면 단순히 최적화되고 있는지 확인하는 것은 정말 쉽습니다 .

상수 표현식이 필요한 컨텍스트에서 사용하십시오.

int main()
{
    constexpr int test_const = length("abcd");
    std::array<char,length("abcdefgh")> test_const2;
}

4
... -pedanticgcc를 사용하는 경우으로 컴파일하십시오 . 그렇지 않으면, 당신은 어떤 경고 및 오류를 얻을
BЈовић

@ BЈовић 또는 템플릿 인수와 같이 GCC에 잠재적으로 방해가되는 확장이없는 컨텍스트에서 사용하십시오.
문헌 : Angew는 더 이상 자랑 SO의없는

같으면는 \ t 열거 해킹은 더 신뢰할 수? 같은 enum { Whatever = length("str") }?
sharptooth

18
언급의 가치입니다static_assert(length("str") == 3, "");
크리스

8
constexpr auto test = /*...*/;아마도 가장 일반적이고 간단합니다.
TC

19

참고로 최신 컴파일러 (예 : gcc-4.x) strlen는 일반적으로 내장 함수 로 정의되기 때문에 컴파일 타임에 문자열 리터럴에 대해 수행 합니다 . 최적화가 활성화되지 않았습니다. 결과가 컴파일 시간 상수는 아니지만.

예 :

printf("%zu\n", strlen("abc"));

결과 :

movl    $3, %esi    # strlen("abc")
movl    $.LC0, %edi # "%zu\n"
movl    $0, %eax
call    printf

참고,이 작품 때문에 strlen하는 내장 우리가 사용하는 경우 기능 -fno-builtins이 실행시에 그것을 호출로 복귀, 볼 살
Shafik Yaghmour

strlen이다 constexpr도 함께, 나를 위해 -fno-nonansi-builtins(것 같다 -fno-builtins++ 더 이상 g에 존재하지 않습니다). 나는이 할 수 있기 때문에 나는, "constexpr을"말 template<int> void foo();foo<strlen("hi")>(); 4.8.4 - g ++
아론 McDaid

19

재귀 적이 지 않고 컴파일 타임에 문자열의 길이를 계산하는 또 다른 함수를 제안하겠습니다.

template< size_t N >
constexpr size_t length( char const (&)[N] )
{
  return N-1;
}

ideone 에서이 샘플 코드를 살펴보십시오 .


5
포함 된 '\ 0'으로 인해 strlen과 같지 않을 수 있습니다. strlen ( "hi \ 0there")! = length ( "hi \ 0there")
unkulunkulu

이것이 올바른 방법입니다. 이것은 Effective Modern C ++의 예입니다 (올바르게 기억한다면). 그러나 완전히 constexpr 인 멋진 문자열 클래스가 있습니다 .Scott Schurr의 str_const , 아마도 이것은 더 유용 할 것입니다 (그리고 C 스타일은 적습니다).
QuantumKarl

@MikeWeir Ops, 이상합니다. 여기에 다양한 링크는 다음과 같습니다 질문에 대한 링크 , 종이 링크 , 자식에 대한 소스에 링크
QuantumKarl

지금 char temp[256]; sprintf(temp, "%u", 2); if(1 != length(temp)) printf("Your solution doesn't work"); yow
Pablo Ariel 19

7

constexpr합당한 컴파일러가 활성화 된 적절한 최적화 수준에서 수행 할 것이지만 함수가 컴파일 타임에 평가 된다는 보장은 없습니다 . 반면에, 템플릿 매개 변수 컴파일 타임에 평가 되어야합니다 .

컴파일 타임에 강제로 평가하기 위해 다음 트릭을 사용했습니다. 불행히도 정수 값으로 만 작동합니다 (즉, 부동 소수점 값으로는 작동하지 않음).

template<typename T, T V>
struct static_eval
{
  static constexpr T value = V;
};

자, 쓰면

if (static_eval<int, length("hello, world")>::value > 7) { ... }

if명령문이 런타임 오버 헤드가없는 컴파일 타임 상수 임을 확인할 수 있습니다 .


8
또는 std :: integral_constant <int, length (...)> :: value
Mircea Ispas

1
어쨌든 컴파일 타임 lenconstexpr수단 length이 평가되어야 하기 때문에 예제는 약간 무의미 합니다.
chris

@ 크리스 나는 몰랐다 있어야합니다 나는 그것이 것을 관찰하지만, 수 있습니다 내 컴파일러.
5gon12eder 2014 년

좋아, 다른 대부분의 답변에 따르면 예를 덜 무의미하게 수정했습니다. 사실, 그것은 if내가 원래 트릭을 사용한 조건 (컴파일러가 데드 코드 제거를 수행하는 것이 필수적인 경우)이었습니다.
5gon12eder 2014 년

1

일반화 상수 표현식 에 대한 Wikipedia 항목의 간단한 설명 :

함수에 constexpr을 사용하면 해당 함수가 수행 할 수있는 작업에 몇 가지 제한이 있습니다. 첫째, 함수는 무효가 아닌 반환 유형을 가져야합니다. 둘째, 함수 본문은 변수를 선언하거나 새로운 유형을 정의 할 수 없습니다. 셋째, 본문에는 선언, null 문 및 단일 return 문만 포함될 수 있습니다. 인수 대체 후 return 문의식이 상수 식을 생성하는 인수 값이 있어야합니다.

constexpr함수 정의 앞에 키워드가 있으면 컴파일러가 이러한 제한 사항이 충족되는지 확인하도록 지시합니다. 그렇다면 상수를 사용하여 함수를 호출하면 반환 된 값이 상수임을 보장하므로 상수 표현식이 필요한 모든 곳에서 사용할 수 있습니다.


이러한 조건 은 반환 된 값이 일정하다고 보장하지 않습니다 . 예를 들어, 함수는 다른 인수 값으로 호출 될 수 있습니다.
Ben Voigt 2014 년

맞아, @BenVoigt. 상수 표현으로 호출되도록 편집했습니다.
kaedinger 2014 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.