static_assert
에서 를 사용하려면 if constexpr
일부 템플릿 매개 변수에 따라 조건을 만들어야합니다. 흥미롭게도 코드가 람다로 싸여 있으면 gcc와 clang이 동의하지 않습니다.
다음 코드는 gcc로 컴파일되지만 clang은 if constexpr
true 일 수 없더라도 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
하지만 확실하지 않습니다.
답변
가입일 [stmt.if / 2 (강조 광산)
if 구문이 if constexpr 형식 인 경우 조건 값은 문맥 상 변환 된 bool 유형의 상수 표현식이어야합니다. 이 형식을 constexpr if 문이라고합니다. 변환 된 조건의 값이 false이면 첫 번째 하위 명령문은 삭제 된 명령문이고, 그렇지 않으면 두 번째 하위 명령문이있는 경우 폐기 된 명령문입니다. 둘러싸는 템플릿 엔티티 ([temp.pre])의 인스턴스화 중에, 인스턴스화 후 조건이 값에 의존하지 않으면, 폐기 된 서브 스테이트먼트 (있는 경우)는 인스턴스화되지 않습니다.
정적 어설 션이 삭제 될 것이라고 생각하지만, 그렇지는 않습니다.
컴파일러가 항상 거짓임을 알고 있기 때문에 정적 어설 션은 템플릿의 첫 번째 단계에서 트리거됩니다.
가입일 [temp.res] / 8 (강조 광산)
인스턴스화 전에 템플릿의 유효성을 확인할 수 있습니다. [ 참고 : 어떤 이름이 유형 이름인지 알면 모든 템플릿의 구문을 이런 방식으로 확인할 수 있습니다. — 끝 주 ] 다음과 같은 경우 프로그램이 잘못 구성되고 진단이 필요하지 않습니다.
- (8.1) 템플릿과 템플릿 내의 명령문이 인스턴스화되지 않은 경우 템플릿 또는 constexpr의 하위 문장에 대해 유효한 전문화를 생성 할 수 없습니다 . 또는
[…]
예, 귀하의 False<T>
의지에 따라 다릅니다 T
. 문제는 일반 람다는 그 자체가 템플릿이며 False<T>
람다의 템플릿 매개 변수에 의존하지 않는다는 것입니다.
A의 T
그 False<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>{});
}
답변
여기서 일반적인 규칙은 [temp.res] / 8입니다 .
다음과 같은 경우 프로그램이 잘못 구성되고 진단이 필요하지 않습니다.
인스턴스화되면 foo<T>
는 static_assert
당신이 더 이상 의존하지 않습니다. 그것은 static_assert(false)
일반적인 람다의 호출 연산자의 모든 가능한 인스턴스화에 대해된다 f
. 진단이 불필요하며 형식이 잘못되었습니다. Clang 진단, gcc는 그렇지 않습니다. 둘 다 맞습니다.
는 것이 중요하지 않습니다 static_assert
여기이 되어 버려.
로 대체
False<T>
하여 쉽게 고칠 수 있습니다False<decltype(x)>
.
이것은 static_assert
일반적인 람다 내 에서 의존성을 유지 하고 이제는 유효한 전문화가있을 수있는 상태가되어 더 이상 잘못 형성되지 않습니다.