constexpr 인 경우-왜 폐기 된 문이 완전히 검사됩니까?


14

GCC 10에서 c ++ 20 consteval을 어지럽히고이 코드를 작성했습니다.

#include <optional>
#include <tuple>
#include <iostream>

template <std::size_t N, typename Predicate, typename Tuple>
consteval std::optional<std::size_t> find_if_impl(Predicate&& pred,
                                                  Tuple&& t) noexcept {
  constexpr std::size_t I = std::tuple_size_v<std::decay_t<decltype(t)>> - N;

  if constexpr (N == 0u) {
    return std::nullopt;
  } else {
    return pred(std::get<I>(t))
               ? std::make_optional(I)
               : find_if_impl<N - 1u>(std::forward<decltype(pred)>(pred),
                                      std::forward<decltype(t)>(t));
  }
}

template <typename Predicate, typename Tuple>
consteval std::optional<std::size_t> find_if(Predicate&& pred,
                                             Tuple&& t) noexcept {
  return find_if_impl<std::tuple_size_v<std::decay_t<decltype(t)>>>(
      std::forward<decltype(pred)>(pred), std::forward<decltype(t)>(t));
}

constexpr auto is_integral = [](auto&& x) noexcept {
    return std::is_integral_v<std::decay_t<decltype(x)>>;
};


int main() {
    auto t0 = std::make_tuple(9, 1.f, 2.f);
    constexpr auto i = find_if(is_integral, t0);
    if constexpr(i.has_value()) {
        std::cout << std::get<i.value()>(t0) << std::endl;
    }
}

STL 찾기 알고리즘처럼 작동하지만 튜플에서 반복자를 반환하는 대신 컴파일 타임 조건자를 기반으로 선택적 인덱스를 반환합니다. 이제이 코드는 잘 컴파일되고 출력됩니다.

9

그러나 튜플에 정수 유형 인 요소가 포함되어 있지 않으면 i.value ()가 여전히 빈 옵션에서 호출되므로 프로그램이 컴파일되지 않습니다. 왜 그럴까요?



그래도 해결되지 않는 @AndyG? x)
Yamahari

답변:


11

이것이 constexpr이 작동 하는 방식입니다. [stmt.if] / 2를 확인하면

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

강조 광산

따라서 템플릿에 있고 조건에 따라 값이 달라지면 버려진 식만 평가하지 않습니다. main함수 템플릿이 아니기 때문에 컴파일러에서 if 문의 본문이 여전히 올바른지 검사합니다.

Cppreference는 constexpr에 관한 섹션에서 다음과 같이 말합니다.

constexpr if 문이 템플릿 화 된 엔터티 내부에 나타나고 인스턴스화 후 조건에 따라 값이 달라지지 않으면 포함 된 템플릿이 인스턴스화 될 때 삭제 된 문이 인스턴스화되지 않습니다.

template<typename T, typename ... Rest>
void g(T&& p, Rest&& ...rs) {
    // ... handle p
    if constexpr (sizeof...(rs) > 0)
        g(rs...); // never instantiated with an empty argument list.
}

템플리트 외부에서 폐기 된 명령문이 완전히 점검됩니다. constexpr이 #if 전처리 지시문을 대체하지 않는 경우 :

void f() {
    if constexpr(false) {
        int i = 0;
        int *p = i; // Error even though in discarded statement
    }
}

이것에 대한 추론을 알고 있습니까? constexpr 인 경우 이것이 적합 할 것 같습니다. 또한 해결책은 어떻게 든 템플릿에 포장하는 것입니까?
Yamahari

@Yamahari C ++ 템플릿은 원하는 것보다 더 구조적으로 구성되어 있기 때문입니다. 그리고 네, 그것을 템플릿으로 감싸십시오 (또는 이와 같이 쓰십시오 i.value_or(0))
Barry

2
@Yamahari 예, 해결책은 코드를 함수 템플릿에 넣는 것입니다. 추론이 진행되는 한 나는 이유를 모른다. 그것은 아마도 좋은 질문 일 것입니다.
NathanOliver

@Barry value_or (0) 벌금을 작동하지만 경우에 튜플의 크기가 0 인 경우
Yamahari

@ 야마하 리 그래 ... 내 의견이 좋지 않습니다.
Barry
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.