se constexpr - perché l'istruzione scartata è completamente controllata?


14

Stavo scherzando con cteptte 20 ++ in GCC 10 e ho scritto questo codice

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

Che dovrebbe funzionare come l'algoritmo find STL ma su tuple e invece di restituire un iteratore, restituisce un indice opzionale basato su un predicato del tempo di compilazione. Ora questo codice viene compilato correttamente e viene stampato

9

Ma se la tupla non contiene un elemento che è un tipo integrale, il programma non viene compilato, poiché i.value () viene ancora chiamato su un facoltativo vuoto. Ora perché?



@AndyG che non lo risolve, vero? x)
Yamahari

Risposte:


11

Questo è il modo in cui constexpr funziona. Se controlliamo [stmt.if] / 2

Se l'istruzione if ha la forma if constexpr, il valore della condizione deve essere un'espressione costante convertita contestualmente di tipo bool; questo modulo è chiamato istruzione constexpr if. Se il valore della condizione convertita è falso, il primo sottostatement è un'istruzione scartata, altrimenti il ​​secondo sottostatement, se presente, è un'istruzione scartata. Durante l'istanza di un'entità modello racchiusa ([temp.pre]), se la condizione non dipende dal valore dopo la sua istanza, il sottostatement scartato (se presente) non viene istanziato. [...]

enfatizzare il mio

Quindi possiamo vedere che non valutiamo l'espressione scartata solo se ci troviamo in un modello e se la condizione dipende dal valore. mainnon è un modello di funzione, quindi il corpo dell'istruzione if è ancora verificato dal compilatore per la correttezza.

Cppreference lo dice anche nella loro sezione su constexpr se con:

Se un'istruzione constexpr if appare all'interno di un'entità templata e se la condizione non è dipendente dal valore dopo l'istanza, l'istruzione scartata non viene istanziata quando viene istanziata la maschera racchiusa.

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

Al di fuori di un modello, un'istruzione scartata è completamente controllata. se constexpr non sostituisce la direttiva di preelaborazione #if:

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

conosci il ragionamento per questo? sembra che questo sarebbe adatto per constexpr. Inoltre, la soluzione sarebbe ad esempio avvolgerla in un modello in qualche modo?
Yamahari

@Yamahari Perché i modelli C ++ sono sia più che meno strutturati di quanto tu voglia che siano. E sì, avvolgilo in un modello (o scrivi come i.value_or(0))
Barry,

2
@Yamahari Sì, la soluzione sarebbe quella di inserire il codice in un modello di funzione. Per quanto riguarda il ragionamento, non so perché. Sarebbe probabilmente una buona domanda da porre.
NathanOliver,

@Barry value_or (0) funziona bene, ma nel caso in cui la tupla sia di dimensioni 0
Yamahari

@Yamahari Sì ... non è un buon suggerimento da parte mia.
Barry,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.