3 방향 비교 및 ​​constexpr 함수 템플릿 : 어떤 컴파일러가 옳은가?

Nov 02 2020

중히 여기다:

#include <compare>

template<class=void>
constexpr int f() { return 1; }

unsigned int x;
using T = decltype(x <=> f());

GCC 및 MSVC는 T. Clang은 다음 오류 메시지와 함께이를 거부합니다.

<source>:7:26: error: argument to 'operator<=>' cannot be narrowed from type 'int' to 'unsigned int'
using T = decltype(x <=> f());
                        ^
1 error generated.

( 라이브 데모 )

template-head ( template<class=void>)가 제거되거나 f선언 전에 명시 적으로 또는 암시 적으로 인스턴스화 된 T경우 Clang은이를 수락합니다. 예를 들어 Clang은 다음을 허용합니다.

#include <compare>

template<class=void>
constexpr int f() { return 1; }

unsigned x;
auto _ = x <=> f();
using T = decltype(x <=> f());

( 라이브 데모 )

어떤 컴파일러가 옳고 그 이유는 무엇입니까?

답변

12 cpplearner Nov 02 2020 at 17:40

Clang은 N4861에 따라 정확 합니다.

[temp.inst] / 5 :

함수 템플릿 전문화가 선언 된 전문화가 아닌 경우, 함수 정의가 존재해야하는 컨텍스트에서 전문화가 참조되거나 정의의 존재가 프로그램의 의미에 영향을주는 경우 함수 템플릿 전문화는 암시 적으로 인스턴스화됩니다.

[temp.inst] / 8 :

변수 또는 함수가 표현식 ([expr.const])에 의한 상수 평가에 필요한 경우 변수 또는 함수의 정의의 존재는 프로그램의 의미에 영향을 미치는 것으로 간주됩니다.

[expr.const] / 15 :

다음과 같은 경우 상수 평가를 위해 함수 또는 변수가 필요합니다 .

  • 상수로 평가 될 수있는 표현식 ([basic.def.odr])에 의해 명명 된 constexpr 함수 또는
  • 변수 [...].

[expr.const] / 15 :

표현식 또는 변환은 다음과 같은 경우 잠재적으로 상수로 평가 됩니다.

  • 명백히 지속적으로 평가되는 표현,
  • 잠재적으로 평가 된 표현식 ([basic.def.odr]),
  • 중괄호 초기화 목록의 즉각적인 하위 표현,
  • 템플릿 엔티티 내에서 발생하는 양식 및 캐스트 표현식의 표현식 또는
  • 중첩 된 평가되지 않은 피연산자의 하위식이 아닌 위 중 하나의 하위 식입니다.

[expr.const] / 5 :

추상 기계 ([intro.execution])의 규칙에 따라 E 의 평가가 다음 중 하나를 평가하지 않는 한 표현식 E 는 핵심 상수 표현식 입니다.

  • [...]
  • 정의되지 않은 constexpr 함수의 호출;

[dcl.init.list] / 7 :

축소 변환은 암시 적 변환 되나

  • [...]
  • 정수 유형 또는 범위가 지정되지 않은 열거 유형에서 원본 유형의 모든 값을 나타낼 수없는 정수 유형으로, 단, 정수 승격 후 값이 대상 유형에 맞는 상수 표현식 인 경우는 예외입니다.

[expr.spaceship] / 4 :

두 피연산자 모두 산술 유형이 있거나 한 피연산자에 정수 유형이 있고 다른 피연산자에 범위가 지정되지 않은 열거 유형이있는 경우 일반적인 산술 변환이 피연산자에 적용됩니다. 그때:

  • 정수 유형에서 부동 소수점 유형으로의 축소 변환이 필요한 경우 프로그램의 형식이 잘못되었습니다.

[expr.arith.conv] :

[T] 일반적인 산술 변환 [...]은 다음과 같이 정의됩니다.

  • [...]
  • 그렇지 않으면 적분 프로모션 ([conv.prom])이 두 피연산자 모두에서 수행됩니다. 그런 다음 승격 된 피연산자에 다음 규칙이 적용됩니다.
    • [...]
    • 그렇지 않으면 부호없는 정수형을 가진 피연산자가 다른 피연산자의 유형보다 크거나 같은 순위를 가질 경우 부호있는 정수형을 가진 피연산자는 부호없는 정수형을 가진 피연산자의 유형으로 변환됩니다.

이후 x <=> f()에 decltype(x <=> f())"평가 잠재적 일정한"이라는 조건을 만족시키지 않는, f"상수 평가 필요"하지 않는다. 따라서 정의의 존재 f<>는 프로그램의 의미에 영향을 미치지 않는 것으로 간주됩니다. 따라서이 식은의 정의를 인스턴스화하지 않습니다 f<>.

따라서 원래 예제에서는 f()상수 표현식이 아닌 정의되지 않은 constexpr 함수에 대한 호출입니다.

일반적인 산술 변환에 따라 x <=> f(), f()(유형의 int)는 unsigned int. 시 f()상수 식없고,이 변환은 잘못 형성된 프로그램을 렌더링 축소 변환한다.

경우 f함수 서식 없거나 그 정의가 인스턴스화 되었다면, f() 인 일정한 발현 및 결과 때문 f()에 맞는 unsigned int에서 변환 f()을가 unsigned int축소 변환되지 않고, 따라서 프로그램은 잘 형성된다.