qual è il caso d'uso di esplicito (bool)


24

C ++ 20 ha introdotto esplicito (bool) che seleziona in modo condizionale in fase di compilazione se un costruttore viene reso esplicito o meno.

Di seguito è riportato un esempio che ho trovato qui .

struct foo {

  // Specify non-integral types (strings, floats, etc.) require explicit construction.

  template <typename T>

  explicit(!std::is_integral_v<T>) foo(T) {}

};

foo a = 123; // OK

foo b = "123"; // ERROR: explicit constructor is not a candidate (explicit specifier evaluates to true)

foo c {"123"}; // OK

Qualcuno può dirmi qualsiasi altro caso explicit (bool)d'uso diverso dall'uso std::is_integral?


1
Un esempio è che diventa molto più facile implementare costruttori esplicitamente condizionati come quelli tuplecon questa funzione.
Pretorio

1
Non è una risposta corretta, ma potresti anche guardare la motivazione nel documento che l'ha introdotta: wg21.link/p0892
N. Shead

Esempio: (insieme ai concetti) riduce il numero richiesto di classi base per implementare un costruttore di copie esplicitamente condizionatamente fornito da 3 a 0.
LF

Risposte:


21

La motivazione stessa può essere vista nel documento .

È necessario rendere esplicitamente condizionali i costruttori. Cioè, vuoi:

pair<string, string> safe() {
    return {"meow", "purr"}; // ok
}

pair<vector<int>, vector<int>> unsafe() {
    return {11, 22}; // error
}

Il primo va bene, quei costruttori sono impliciti. Ma quest'ultimo sarebbe male, lo sono quei costruttori explicit. Con C ++ 17 (o C ++ 20 con concetti), l'unico modo per farlo funzionare è scrivere due costruttori: uno explicite uno no:

template <typename T1, typename T2>
struct pair {
    template <typename U1=T1, typename U2=T2,
        std::enable_if_t<
            std::is_constructible_v<T1, U1> &&
            std::is_constructible_v<T2, U2> &&
            std::is_convertible_v<U1, T1> &&
            std::is_convertible_v<U2, T2>
        , int> = 0>
    constexpr pair(U1&&, U2&& );

    template <typename U1=T1, typename U2=T2,
        std::enable_if_t<
            std::is_constructible_v<T1, U1> &&
            std::is_constructible_v<T2, U2> &&
            !(std::is_convertible_v<U1, T1> &&
              std::is_convertible_v<U2, T2>)
        , int> = 0>
    explicit constexpr pair(U1&&, U2&& );    
};  

Questi sono quasi interamente duplicati e le definizioni di questi costruttori sarebbero identiche.

Con explicit(bool), puoi semplicemente scrivere un singolo costruttore - con la parte esplicitamente condizionata della costruzione localizzata al solo explicit-specificatore:

template <typename T1, typename T2>
struct pair {
    template <typename U1=T1, typename U2=T2,
        std::enable_if_t<
            std::is_constructible_v<T1, U1> &&
            std::is_constructible_v<T2, U2>
        , int> = 0>
    explicit(!std::is_convertible_v<U1, T1> ||
        !std::is_convertible_v<U2, T2>)
    constexpr pair(U1&&, U2&& );   
};

Questo abbina meglio l'intento, è molto meno codice da scrivere ed è meno lavoro da fare per il compilatore durante la risoluzione del sovraccarico (poiché ci sono meno costruttori tra cui scegliere).


1
C ++ 20 offre anche la possibilità di cambiare la enable_if_tparte in un vincolo più bello e più semplice, possibilmente usando concetti. Ma questo è il punto di questa domanda.
aschepler

2

Un altro possibile utilizzo che vedo è con il modello variadic:

È generalmente buono, per impostazione predefinita, avere explicitper costruttore con un solo argomento (a meno che non sia desiderata la conversione).

così

struct Foo
{
    template <typename ... Ts>
    explicit(sizeof...(Ts) == 1) Foo(Ts&&...);

    // ...
};

0

Ho potuto vedere un caso d'uso per richiedere in modo explicitcondizionale quando l'input potrebbe essere di tipo simile a una vista (puntatore non elaborato std::string_view) a cui il nuovo oggetto si attaccherà dopo la chiamata (solo copiando la vista, non ciò a cui si riferisce, rimanendo dipendente da la durata dell'oggetto visualizzato) o potrebbe essere un tipo simile al valore (diventa proprietario di una copia, senza dipendenze esterne della durata).

In una situazione del genere, il chiamante è responsabile di mantenere in vita l'oggetto visualizzato (la chiamata possiede una vista, non l'oggetto originale) e la conversione non dovrebbe essere effettuata in modo implicito, perché rende troppo facile per l'oggetto creato implicitamente sopravvivere all'oggetto visualizzato. Al contrario, per i tipi di valore, il nuovo oggetto riceverà la propria copia, quindi mentre la copia potrebbe essere costosa, non commetterà un errore nel codice se si verifica una conversione implicita.

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.