Herb Sutter's Ritorno alle origini! La presentazione di Essentials of Modern C++ in CppCon ha discusso diverse opzioni per il passaggio dei parametri e ha confrontato le loro prestazioni rispetto alla facilità di scrittura/insegnamento. L'opzione 'avanzato' (che fornisce le migliori prestazioni in tutti i casi testati, ma troppo difficile per la maggior parte degli sviluppatori di scrivere) è stato l'inoltro perfetto, con l'esempio dato (PDF, pg. 28):Qual è il vincolo `enable_if` corretto su un inoltro perfetto?
class employee {
std::string name_;
public:
template <class String,
class = std::enable_if_t<!std::is_same<std::decay_t<String>,
std::string>::value>>
void set_name(String &&name) noexcept(
std::is_nothrow_assignable<std::string &, String>::value) {
name_ = std::forward<String>(name);
}
};
L'esempio utilizza una funzione template con l'inoltro riferimento, con il parametro modello String
vincolato utilizzando enable_if
. Tuttavia, il vincolo sembra essere errato: sembra voler dire che questo metodo può essere utilizzato solo se il tipo String
non è un std::string
, il che non ha senso. Ciò significherebbe che questo membro std::string
può essere impostato utilizzando qualsiasi valore tranne e un valore std::string
.
using namespace std::string_literals;
employee e;
e.set_name("Bob"s); // error
Una spiegazione ho ritenuto fosse che c'è un semplice errore di battitura e il vincolo era destinato ad essere std::is_same<std::decay_t<String>, std::string>::value
invece di !std::is_same<std::decay_t<String>, std::string>::value
. Tuttavia ciò implicherebbe che il setter non funzioni con, per esempio, const char *
e ovviamente era destinato a funzionare con questo tipo dato che questo è uno dei casi testati nella presentazione.
Mi sembra che il vincolo corretta è più simile a:
consentendo tutto ciò che può essere assegnato al membro per essere utilizzato con il setter.
Ho il vincolo giusto? Ci sono altri miglioramenti che possono essere apportati? C'è qualche spiegazione per il vincolo originale, forse è stato preso fuori dal contesto?
Inoltre mi chiedo se le complicate parti, 'unteachable' di questa dichiarazione sono davvero così vantaggioso. Dal momento che non stiamo usando sovraccaricare possiamo semplicemente fare affidamento sulla normale modello di istanza:
template <class String>
void set_name(String &&name) noexcept(
std::is_nothrow_assignable<decltype((name_)), String>::value) {
name_ = std::forward<String>(name);
}
E naturalmente c'è un certo dibattito sul fatto se noexcept
conta davvero, un po 'dicendo di non preoccuparsi troppo su di esso tranne che per spostare/primitive di swap :
template <class String>
void set_name(String &&name) {
name_ = std::forward<String>(name);
}
Forse con i concetti non sarebbe irragionevolmente difficile vincolare il modello, solo per i messaggi di errore migliorati.
template <class String>
requires std::is_assignable<decltype((name_)), String>::value
void set_name(String &&name) {
name_ = std::forward<String>(name);
}
Questo sarebbe ancora degli inconvenienti che non può essere virtuale e che deve essere in un'intestazione (anche se si spera moduli finiranno rendering che discutibile), ma questo sembra abbastanza insegnabile.
Beh, qualcuno nella stanza ha detto che si tratta di un vincolo non corretto e non accetta stringhe letterali - forse possiamo cc @HowardHinnant? Non sembra nemmeno giusto per me. [Video del talk] (http://youtu.be/xnqTKD8uD64?t=1h15m21s) – dyp
@PiotrS .: MSVC non ha alias, quindi ho dimenticato:/ –
@MooingDuck Hey, VS2013 supporta gli alias e i loro strumenti stdlib gli alias di tipo_trait C++ 14. – bames53