람다에서 static_assert가있는 constexpr이 올바른 컴파일러는 무엇입니까?


13

static_assert에서 를 사용하려면 if constexpr일부 템플릿 매개 변수에 따라 조건을 만들어야합니다. 흥미롭게도 코드가 람다로 싸여 있으면 gcc와 clang이 동의하지 않습니다.

다음 코드는 gcc로 컴파일되지만 clang은 if constexprtrue 일 수 없더라도 assert를 트리거 합니다.

#include <utility>

template<typename T> constexpr std::false_type False;

template<typename T>
void foo() {

    auto f = [](auto x) {
        constexpr int val = decltype(x)::value;
        if constexpr(val < 0) {
            static_assert(False<T>, "AAA");
        }
    };

    f(std::integral_constant<int, 1>{});
}

int main() {
    foo<int>();
}

여기에 실례가 있습니다 .

로 대체 False<T>하여 쉽게 고칠 수 있습니다 False<decltype(x)>.

문제는 어떤 컴파일러가 옳습니까? 의 조건 static_assert이에 의존 하기 때문에 gcc가 정확하다고 가정 T하지만 확실하지 않습니다.


이것은 같은 질문을하지만 반대 방향에서 오는 것입니다 : stackoverflow.com/questions/59393908/… .
NathanOliver

1
@mfnx 재생할 수 없습니다 . 예를 들어 줄 수 있습니까?
NathanOliver

2
나는 둘 다 옳다고 말하고 (잘못 형성된 NDR) : 람다 내부 static_assert(False<int>, "AAA");와 같습니다 static_assert(false, "AAA");.
Jarod42

2
@mfnx 상수 값을 변경했습니다. 상수가 f(std::integral_constant<int, 1>{});Wandbox 인 OP의 예를 사용하면 어설트가 트리거되지 않습니다. wandbox.org/permlink/UFYAmYwtt1ptsndr
NathanOliver

1
@NathanOliver 네, 그렇습니다. 소음이 유감입니다. 상수> = 0 인 경우 constexpr의 해당 코드를 삭제해야하므로 gcc가 올바른 것 같습니다.
mfnx

답변:


1

가입일 [stmt.if / 2 (강조 광산)

if 구문이 if constexpr 형식 인 경우 조건 값은 문맥 상 변환 된 bool 유형의 상수 표현식이어야합니다. 이 형식을 constexpr if 문이라고합니다. 변환 된 조건의 값이 false이면 첫 번째 하위 명령문은 삭제 된 명령문이고, 그렇지 않으면 두 번째 하위 명령문이있는 경우 폐기 된 명령문입니다. 둘러싸는 템플릿 엔티티 ([temp.pre])의 인스턴스화 중에, 인스턴스화 후 조건이 값에 의존하지 않으면, 폐기 된 서브 스테이트먼트 (있는 경우)는 인스턴스화되지 않습니다.

정적 어설 션이 삭제 될 것이라고 생각하지만, 그렇지는 않습니다.

컴파일러가 항상 거짓임을 알고 있기 때문에 정적 어설 션은 템플릿의 첫 번째 단계에서 트리거됩니다.

가입일 [temp.res] / 8 (강조 광산)

인스턴스화 전에 템플릿의 유효성을 확인할 수 있습니다. [ 참고 : 어떤 이름이 유형 이름인지 알면 모든 템플릿의 구문을 이런 방식으로 확인할 수 있습니다. — 끝 주 ] 다음과 같은 경우 프로그램이 잘못 구성되고 진단이 필요하지 않습니다.

  • (8.1) 템플릿과 템플릿 내의 명령문이 인스턴스화되지 않은 경우 템플릿 또는 constexpr의 하위 문장에 대해 유효한 전문화를 생성 할 수 없습니다 . 또는

[...]

예, 귀하의 False<T>의지에 따라 다릅니다 T. 문제는 일반 람다는 그 자체가 템플릿이며 False<T>람다의 템플릿 매개 변수에 의존하지 않는다는 것입니다.

A의 TFalse<T>거짓이, 정적 어설 션은 항상 거짓이 될 것이다 상관없이 어떤 템플릿 인수 람다로 전송됩니다.

컴파일러는 template의 인스턴스화에 operator()대해 정적 어설 션이 항상 현재 T에 대해 트리거 됨을 알 수 있습니다 . 따라서 컴파일러 오류가 발생합니다.

이에 대한 해결책은 다음에 의존하는 것입니다 x.

template<typename T>
void foo() {

    auto f = [](auto x) {
        if constexpr(x < 0) {
            static_assert(False<decltype(x)>, "AAA");
        }
    };

    f(std::integral_constant<int, 1>{});
}

라이브 예


13

여기서 일반적인 규칙은 [temp.res] / 8입니다 .

다음과 같은 경우 프로그램이 잘못 구성되고 진단이 필요하지 않습니다.

인스턴스화되면 foo<T>static_assert당신이 더 이상 의존하지 않습니다. 그것은 static_assert(false)일반적인 람다의 호출 연산자의 모든 가능한 인스턴스화에 대해된다 f. 진단이 불필요하며 형식이 잘못되었습니다. Clang 진단, gcc는 그렇지 않습니다. 둘 다 맞습니다.

는 것이 중요하지 않습니다 static_assert여기이 되어 버려.

로 대체 False<T>하여 쉽게 고칠 수 있습니다 False<decltype(x)>.

이것은 static_assert일반적인 람다 내 에서 의존성을 유지 하고 이제는 유효한 전문화가있을 수있는 상태가되어 더 이상 잘못 형성되지 않습니다.

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