Classe enable_if

Crea un'istanza di un tipo in modo condizionale per la risoluzione dell'overload SFINAE. Il typedef annidato enable_if<Condition,Type>::type esiste, ed è sinonimo di Type, solo ed esclusivamente se Condition è true.

Sintassi

template <bool B, class T = void>
struct enable_if;

Parametri

B
Valore che determina l'esistenza del tipo risultante.

T
Tipo di cui creare un'istanza se B è true.

Osservazioni:

Se B è true, enable_if<B, T> ha un typedef annidato denominato "type" sinonimo di T.

Se B è false, enable_if<B, T> non ha un typedef annidato denominato "type".

Viene fornito questo modello di alias:

template <bool B, class T = void>
using enable_if_t = typename enable_if<B,T>::type;

In C++, l'errore di sostituzione dei parametri del modello non è un errore in sé. Questo errore viene definito SFINAE (l'errore di sostituzione non è un errore). In genere enable_if viene usato per rimuovere candidati dalla risoluzione dell'overload, ovvero seleziona il set di overload, in modo da consentire il rifiuto di una definizione a favore di un'altra. Questo è conforme al comportamento di SFINAE. Per altre informazioni su SFINAE, vedere L'errore di sostituzione non è un errore su Wikipedia.

Ecco quattro scenari di esempio:

  • Scenario 1: wrapping del tipo restituito di una funzione:
    template <your_stuff>
typename enable_if<your_condition, your_return_type>::type
    yourfunction(args) {// ...
}
// The alias template makes it more concise:
    template <your_stuff>
enable_if_t<your_condition, your_return_type>
yourfunction(args) {// ...
}
  • Scenario 2: aggiunta di un parametro di funzione che include un argomento predefinito:
    template <your_stuff>
your_return_type_if_present
    yourfunction(args, enable_if_t<your condition, FOO> = BAR) {// ...
}
  • Scenario 3: aggiunta di un parametro di modello che include un argomento predefinito:
    template <your_stuff, typename Dummy = enable_if_t<your_condition>>
rest_of_function_declaration_goes_here
  • Scenario 4: se la funzione include un argomento non basato su modello, è possibile eseguire il wrapping del relativo tipo:
    template <typename T>
void your_function(const T& t,
    enable_if_t<is_something<T>::value, const string&>
s) {// ...
}

Lo scenario 1 non funziona con i costruttore e gli operatori di conversione poiché questi non dispongono di tipi restituiti.

Lo scenario 2 lascia il parametro non denominato. È possibile specificare ::type Dummy = BAR, ma il nome Dummy è irrilevante ed è probabile che l'assegnazione di un nome generi un avviso di tipo "parametro senza riferimento". È necessario scegliere un tipo di parametro di funzione FOO e l'argomento BAR predefinito. È possibile specificare int e 0, ma gli utenti del codice potrebbero accidentalmente passare un intero supplementare alla funzione, che verrebbe ignorato. È invece consigliabile usare void ** e 0 o nullptr perché quasi nulla è convertibile in void **:

template <your_stuff>
your_return_type_if_present
yourfunction(args, typename enable_if<your_condition, void **>::type = nullptr) {// ...
}

Lo scenario 2 funziona anche per i costruttori normali. Non è invece adatto agli operatori di conversione, che non accettano parametri supplementari. Non funziona anche per variadic i costruttori perché l'aggiunta di parametri aggiuntivi rende il pacchetto di parametri della funzione un contesto non dedotto e quindi sconfigge lo scopo di enable_if.

Lo scenario 3 usa il nome Dummy, ma è facoltativo. In realtà funziona anche solo "typename = typename", ma se lo si ritiene strano, si può usare un nome "fittizio" qualsiasi, purché non sia un nome che potrebbe essere usato nella definizione di funzione. Se non si assegna un tipo a enable_if, l'impostazione predefinita è void, un risultato del tutto ragionevole poiché Dummy non è rilevante. Questo funziona per tutti gli elementi, inclusi operatori di conversione e variadic costruttori.

Lo scenario 4 funziona per i costruttori che dispongono di tipi restituiti e quindi risolve la limitazione di wrapping dello scenario 1. Lo scenario 4, tuttavia, è limitato agli argomenti di funzione non basati su modello, che non sempre sono disponibili (l'uso dello scenario 4 su un argomento di funzione basato su modello impedisce il funzionamento della deduzione di argomenti del modello su di esso).

L'oggetto enable_if è potente, ma è anche pericoloso se usato in modo improprio. Dato che il suo scopo consiste nell'eliminazione di candidati prima della risoluzione dell'overload, se viene usato in modo improprio i suoi effetti possono essere molto ambigui. Di seguito sono elencati alcuni suggerimenti:

  • Non usare enable_if per selezionare tra implementazioni in fase di compilazione. Non scrivere mai un enable_if per CONDITION e un altro per !CONDITION. Usare invece un criterio di invio tag, ad esempio un algoritmo che seleziona le implementazioni in base ai punti di forza degli iteratori assegnati.

  • Non usare enable_if per applicare i requisiti. Se si desidera convalidare i parametri del modello e se la convalida non riesce, generare un errore anziché selezionare un'altra implementazione, usare static_assert.

  • Usare enable_if quando è presente un set di overload che rende ambiguo un codice altrimenti corretto. Spesso questo accade nei costruttori a conversione implicita.

Esempio

In questo esempio viene illustrato come la funzione del enable_ifmodello della libreria std::make_pair() standard C++ sfrutta .

void func(const pair<int, int>&);

void func(const pair<string, string>&);

func(make_pair("foo", "bar"));

In questo esempio make_pair("foo", "bar") restituisce pair<const char *, const char *>. La risoluzione dell'overload deve stabilire quale func() è richiesto. pair<A, B> ha un costruttore a conversione implicita tratto da pair<X, Y>. Non si tratta di una novità. Era infatti presente in C++98. In C++98/03, tuttavia, la firma del costruttore a conversione implicita esiste sempre, anche se è pair<int, int>(const pair<const char *, const char *>&). Per la risoluzione dell'overload non è rilevante che un tentativo di creare un'istanza del costruttore determini una forte esplosione perché const char * non è implicitamente convertibile in int; vengono cercate solo le firme, prima della creazione di un'istanza delle definizioni di funzione. Il codice di esempio è quindi ambiguo perché sono presenti firme per convertire pair<const char *, const char *> sia in pair<int, int> che in pair<string, string>.

C++11 risolve questa ambiguità usando enable_if per verificare che pair<A, B>(const pair<X, Y>&) esista solo quando const X& è convertibile implicitamente in A e const Y& è convertibile implicitamente in B. Ciò consente alla risoluzione dell'overload di determinare che pair<const char *, const char *> non è convertibile in pair<int, int> e che l'overload che accetta pair<string, string> è fattibile.

Requisiti

Intestazione: <type_traits>

Spazio dei nomi: std

Vedi anche

<type_traits>