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.