Cosa sono i comparatori trasparenti?


106

In C ++ 14, i contenitori associativi sembrano essere cambiati da C ++ 11 - [associative.reqmts] / 13 dice:

I modelli funzione membro find, count, lower_bound, upper_bound, e equal_rangenon devono partecipare alla risoluzione di sovraccarico a meno che il tipo Compare::is_transparentesiste.

Qual è lo scopo di rendere "trasparente" un comparatore?

C ++ 14 fornisce anche modelli di libreria come questo:

template <class T = void> struct less {
    constexpr bool operator()(const T& x, const T& y) const;
    typedef T first_argument_type;
    typedef T second_argument_type;
    typedef bool result_type;
};

template <> struct less<void> {
    template <class T, class U> auto operator()(T&& t, U&& u) const
    -> decltype(std::forward<T>(t) < std::forward<U>(u));
    typedef *unspecified* is_transparent;
};

Così, per esempio, std::set<T, std::less<T>>potrebbe non avere un comparatore trasparente, ma std::set<T, std::less<>> dovrebbe avere uno.

Quale problema risolve e cambia il modo in cui funzionano i contenitori standard? Ad esempio, i parametri del modello di std::setsono ancora Key, Compare = std::less<Key>, ..., così fa il set predefinito perde le sue find, count, membri ecc?


Ad esempio, vedere questa descrizione cppreference . E ora mi sento stupido, perché sto notando la parola " modello di funzione membro " ...
Kerrek SB

5
Possibilmente correlati: stackoverflow.com/questions/18939882/...

Risposte:


60

Quale problema risolve,

Vedi la risposta di Dietmar e la risposta di Remyabel .

e questo cambia il modo in cui funzionano i contenitori standard?

No, non per impostazione predefinita.

I nuovi overload del modello di funzione membro di findecc. Consentono di utilizzare un tipo paragonabile alla chiave del contenitore, invece di utilizzare il tipo di chiave stesso. Vedere N3465 di Joaquín Mª López Muñoz per le motivazioni e una proposta dettagliata e scritta con cura per aggiungere questa funzione.

Alla riunione di Bristol il LWG ha convenuto che la funzione di ricerca eterogenea fosse utile e desiderabile, ma non potevamo essere sicuri che la proposta di Joaquín sarebbe stata sicura in tutti i casi. La proposta N3465 avrebbe causato seri problemi ad alcuni programmi (vedere la sezione Impatto sul codice esistente ). Joaquín ha preparato una bozza di proposta aggiornata con alcune implementazioni alternative con diversi compromessi, il che è stato molto utile per aiutare il LWG a comprendere i pro ei contro, ma tutti rischiavano di rompere alcuni programmi in qualche modo, quindi non c'era consenso per aggiungere la funzione. Abbiamo deciso che, sebbene non sarebbe stato sicuro aggiungere la funzione incondizionatamente, sarebbe stato sicuro se fosse disabilitata per impostazione predefinita e solo "opt in".

La differenza fondamentale della proposta N3657 (che era una revisione dell'ultimo minuto da me e STL basata su N3465 e una bozza inedita successiva di Joaquín) era quella di aggiungere il is_transparenttipo come protocollo che può essere utilizzato per optare per la nuova funzionalità.

Se non si usa un "funtore trasparente" (cioè uno che definisce un is_transparenttipo), i contenitori si comportano come hanno sempre fatto, e questa è ancora l'impostazione predefinita.

Se scegli di utilizzare std::less<>(che è nuovo per C ++ 14) o un altro tipo "funtore trasparente", otterrai la nuova funzionalità.

L'uso std::less<>è facile con i modelli di alias:

template<typename T, typename Cmp = std::less<>, typename Alloc = std::allocator<T>>
  using set = std::set<T, Cmp, Alloc>;

Il nome is_transparentderiva da STL's N3421 che ha aggiunto gli "operatori di diamante" a C ++ 14. Un "funtore trasparente" è uno che accetta qualsiasi tipo di argomento (che non deve essere lo stesso) e inoltra semplicemente quegli argomenti a un altro operatore. Un tale funtore sembra essere esattamente ciò che si desidera per la ricerca eterogenea nei contenitori associativi, quindi il tipo è is_transparentstato aggiunto a tutti gli operatori romboidali e utilizzato come tipo di tag per indicare che la nuova funzionalità deve essere abilitata nei contenitori associativi. Tecnicamente, i contenitori non hanno bisogno di un "funtore trasparente", solo uno che supporti chiamarlo con tipi eterogenei (ad esempio il pointer_comptipo in https://stackoverflow.com/a/18940595/981959 non è trasparente secondo la definizione di STL,pointer_comp::is_transparentconsente di utilizzarlo per risolvere il problema). Se sempre e solo di ricerca nel proprio std::set<T, C>con le chiavi di tipo To intpoi Cha solo bisogno di essere richiamabile con argomenti di tipo Te int(in qualsiasi ordine), che non ha bisogno di essere veramente trasparente. Abbiamo usato quel nome in parte perché non potevamo trovare un nome migliore (avrei preferito is_polymorphicperché tali funtori usano il polimorfismo statico, ma c'è già un std::is_polymorphictratto di tipo che si riferisce al polimorfismo dinamico).


3
Ehi, eri tu quello a cui STL ha detto: "Certo che puoi fare la deduzione dell'argomento del modello nella tua testa" nel discorso collegato a Woolstar?
Kerrek SB

10
No, non c'ero, ma ci sono persone con compilatori molto più conformi nelle loro teste di me :)
Jonathan Wakely

Immagino che "operatore rombo" si riferisca alla <>proposta collegata, ma quella proposta non ha introdotto <>: è la sintassi esistente per un elenco di parametri del modello vuoto. "Funtori operatore diamante" sarebbe un po 'meno confuso.
Qwertie

33

In C ++ 11 non ci sono modelli di membro find(), lower_bound()ecc cioè, nulla è perduto da questo cambiamento. I modelli di membri sono stati introdotti con n3657 per consentire l'utilizzo di chiavi eterogenee con i contenitori associativi. Non vedo alcun esempio concreto in cui ciò sia utile tranne l'esempio che è buono e cattivo!

L' is_transparentutilizzo ha lo scopo di evitare conversioni indesiderate. Se i modelli di membri non fossero vincolati, il codice esistente potrebbe passare direttamente attraverso oggetti che sarebbero stati convertiti senza i modelli di membri. Il caso d'uso di esempio da n3657 è l'individuazione di un oggetto in una std::set<std::string>stringa letterale: con la definizione C ++ 11 std::stringviene costruito un oggetto quando si passa una stringa letterale alla funzione membro corrispondente. Con la modifica è possibile utilizzare direttamente la stringa letterale. Se l'oggetto della funzione di confronto sottostante è implementato esclusivamente in termini di std::stringciò è negativo perché ora std::stringverrebbe creato un per ogni confronto. D'altra parte, se l'oggetto della funzione di confronto sottostante può richiedere un filestd::string e una stringa letterale, che può evitare la costruzione di un oggetto temporaneo.

Il is_transparenttipo annidato nell'oggetto funzione di confronto fornisce un modo per specificare se la funzione membro basata su modelli deve essere utilizzata: se l'oggetto funzione di confronto può trattare argomenti eterogenei, definisce questo tipo per indicare che può trattare argomenti diversi in modo efficiente. Ad esempio, i nuovi oggetti funzione operatore delegano operator<()e dichiarano di essere trasparenti. Questo, almeno, funziona per il std::stringquale ha sovraccaricato meno degli operatori che prendono char const*come argomento. Poiché anche questi oggetti funzione sono nuovi, anche se fanno la cosa sbagliata (cioè richiedono una conversione per qualche tipo), almeno, non sarebbe un cambiamento silenzioso con conseguente degrado delle prestazioni.


Grazie - guarda il mio commento sull'altra domanda: ottieni il comportamento trasparente per impostazione predefinita?
Kerrek SB

8
@KerrekSB: il comportamento trasparente è abilitato quando is_transparentè definito nell'oggetto funzione di confronto secondo 23.2.4 [associative.reqmts] paragrafo 13. Gli oggetti funzione di confronto di default sono std::less<Key>conformi a 23.4.2 [associative.map.syn] e 23.4. 3 [associative.set.syn]. Secondo 20.10.5 [confronto] paragrafo 4, il modello generale per std::less<...>non non definire un tipo nidificato is_transparent, ma la std::less<void>specializzazione fa. Cioè no, non ottieni un operatore trasparente per impostazione predefinita.
Dietmar Kühl

Hai qualche idea per il nome? Voglio dire perché is_transparent?
plasmacel

Vuoi un "esempio concreto in cui questo è utile"? Ecco il mio caso d'uso
spraff

19

Quello che segue è tutto il copy-pasta da n3657 .

D. Qual è lo scopo di rendere "trasparente" un comparatore?

R. Le funzioni di ricerca del contenitore associativo (find, lower_bound, upper_bound, equal_range) accettano solo un argomento di key_type, richiedendo agli utenti di costruire (implicitamente o esplicitamente) un oggetto di key_type per eseguire la ricerca. Questo può essere costoso, ad esempio costruire un oggetto di grandi dimensioni da cercare in un insieme quando la funzione di confronto guarda solo un campo dell'oggetto. C'è un forte desiderio tra gli utenti di essere in grado di cercare utilizzando altri tipi che sono paragonabili a key_type.

D. Quale problema risolve

R. Il LWG aveva dubbi sul codice come il seguente:

std::set<std::string> s = /* ... */;
s.find("key");

In C ++ 11 questo costruirà un singolo std :: string temporaneo e poi lo confronterà con gli elementi per trovare la chiave.

Con la modifica proposta da N3465 la funzione std :: set :: find () sarebbe un modello non vincolato che passerebbe il const char * alla funzione di confronto, std :: less, che costruirà una std :: string temporanea per ogni confronto. Il LWG ha considerato questo problema di prestazioni un problema serio. La funzione find () del modello impedirebbe anche di trovare NULL in un contenitore di puntatori, il che fa sì che il codice precedentemente valido non venga più compilato, ma questo è stato visto come un problema meno serio rispetto alla regressione silenziosa delle prestazioni

D. Questo cambia il modo in cui funzionano i contenitori standard

R. Questa proposta modifica i contenitori associativi in ​​e sovraccaricando le funzioni membro di ricerca con modelli di funzioni membro. Non ci sono cambi di lingua.

D. così il set predefinito perde i suoi membri di ricerca, conteggio, ecc

R. Quasi tutto il codice C ++ 11 esistente non viene modificato perché le funzioni membro non sono presenti a meno che le nuove funzionalità della libreria C ++ 14 non vengano utilizzate come funzioni di confronto.

Per citare Yakk ,

In C ++ 14, std :: set :: find è una funzione modello se Compare :: is_transparent esiste. Il tipo che trasmetti non deve essere necessariamente Key, solo equivalente sotto il tuo comparatore.

e n3657,

Aggiungere il paragrafo 13 in 23.2.4 [associative.reqmts]: La funzione di membro template trovano, lower_bound, upper_bound e equal_range non partecipano alla risoluzione di sovraccarico a meno che il tipo Confronta :: is_transparent non esiste esiste.

n3421 fornisce un esempio di "Funzioni operatore trasparenti" .

Il codice completo è qui .


1
In std::set<std::string>realtà trae vantaggio dal "passaggio char const *attraverso", o hai bisogno di fare un std::set<std::string, std::less<>>?
Kerrek SB

@Kerrek Penso che "passare il char const *" fosse il problema che stavano cercando di evitare, se non mi sbaglio. Guarda la dicitura:With the change proposed by N3465 the std::set::find() function would be an unconstrained template which would pass the const char* through to the comparator function, std::less<std::string>, which would construct a std::string temporary for every comparison. The LWG considered this performance problem to be a serious issue.

La tua citazione e la mia del paragrafo 13 dicono il contrario: "a meno che il tipo non esista / non esista" ...?!
Kerrek SB

4
@KerrekSB, è colpa mia, N3657 avrebbe dovuto dire "esiste" ma io ho scritto "non esiste" ... era un articolo scritto in ritardo all'ultimo minuto. La bozza dello standard è corretta.
Jonathan Wakely

3
Sì, potrebbe essere più chiaro citare ciò che intendevo dire e non ciò che ho effettivamente detto in quel momento :)
Jonathan Wakely

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.