if constexpr – warum wird die verworfene Anweisung vollständig überprüft?

Lesezeit: 3 Minuten

Benutzer-Avatar
Yamahari

Ich habe in GCC 10 mit c++20 Consteval herumgespielt und diesen Code geschrieben

#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

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

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
      std::forward<decltype(pred)>(pred), std::forward<decltype
}

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;
    }
}

Das soll wie der STL-Suchalgorithmus funktionieren, aber auf Tupeln, und anstatt einen Iterator zurückzugeben, gibt es einen optionalen Index zurück, der auf einem Prädikat zur Kompilierzeit basiert. Jetzt lässt sich dieser Code gut kompilieren und ausdrucken

9

Wenn das Tupel jedoch kein Element enthält, das ein ganzzahliger Typ ist, wird das Programm nicht kompiliert, da i.value() immer noch für eine leere Option aufgerufen wird. Nun, warum ist das so?

  • Kleineres Beispiel

    – Artyer

    18. Dezember 2019 um 14:29 Uhr

  • @AndyG das behebt es aber nicht, oder? x)

    – Yamahari

    18. Dezember 2019 um 16:30 Uhr

Benutzer-Avatar
NathanOliver

So geht’s constexpr wenn funktioniert. Wenn wir nachsehen [stmt.if]/2

Wenn die if-Anweisung die Form if constexpr hat, muss der Wert der Bedingung ein kontextabhängig konvertierter konstanter Ausdruck vom Typ bool sein; diese Form wird constexpr if-Anweisung genannt. Wenn der Wert der konvertierten Bedingung falsch ist, ist die erste Unteranweisung eine verworfene Anweisung, andernfalls ist die zweite Unteranweisung, falls vorhanden, eine verworfene Anweisung. Während der Instanziierung einer einschließenden Vorlagenentität ([temp.pre]), wenn die Bedingung nach ihrer Instanziierung nicht wertabhängig ist, wird die verworfene Unteranweisung (sofern vorhanden) nicht instanziiert.[…]

Betonung von mir

Wir sehen also, dass wir den verworfenen Ausdruck nur dann nicht auswerten, wenn wir uns in einer Vorlage befinden und die Bedingung wertabhängig ist. main ist kein Funktions-Template, daher wird der Körper der if-Anweisung noch vom Compiler auf Korrektheit geprüft.

Cpreference sagt dies auch in ihrem Abschnitt über constexpr if with:

Wenn eine constexpr if-Anweisung in einer Entität mit Vorlage erscheint und die Bedingung nach der Instanziierung nicht wertabhängig ist, wird die verworfene Anweisung nicht instanziiert, wenn die einschließende Vorlage instanziiert wird.

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.
}

Außerhalb einer Vorlage wird eine verworfene Anweisung vollständig geprüft. if constexpr ist kein Ersatz für die #if-Vorverarbeitungsanweisung:

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

  • kennst du die Begründung dafür? es sieht so aus, als ob dies gut zu if constexpr passen würde. Also wäre die Lösung zB irgendwie in eine Vorlage zu packen?

    – Yamahari

    18. Dezember 2019 um 15:18 Uhr

  • @Yamahari Weil C++-Vorlagen sowohl mehr als auch weniger strukturiert sind, als Sie es möchten. Und ja, packen Sie es in eine Vorlage (oder schreiben Sie wie i.value_or(0))

    – Barri

    18. Dezember 2019 um 15:26 Uhr


  • @Yamahari Ja, die Lösung wäre, den Code in eine Funktionsvorlage einzufügen. Was die Argumentation angeht, weiß ich nicht warum. Das wäre wahrscheinlich eine gute Frage.

    – NathanOliver

    18. Dezember 2019 um 15:26 Uhr

  • @Barry value_or(0) funktioniert gut, aber für den Fall, dass das Tupel die Größe 0 hat

    – Yamahari

    18. Dezember 2019 um 16:42 Uhr

1012710cookie-checkif constexpr – warum wird die verworfene Anweisung vollständig überprüft?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy