Utilizzo di `std::function<void(…)>` per chiamare una funzione non void
-
27-10-2019 - |
Domanda
Qualche tempo fa ho usato std::function
più o meno così:
std::function<void(int)> func = [](int i) -> int { return i; };
Fondamentalmente, l'ho fatto perché volevo memorizzare diversi oggetti funzione in un file std::function
, ma non volevo limitare i tipi di restituzione di queste funzioni.Dato che sembrava funzionare, l'ho seguito.Ma non sono convinto che sia sicuro da usare e non sono riuscito a trovare alcuna documentazione al riguardo.Qualcuno sa se questo utilizzo è legittimo?O più in generale, quali sono le regole per l'oggetto che può essere tranquillamente assegnato ad a std::function
?
Modificare
Per chiarimenti, il problema che mi preoccupa è che la funzione lambda restituisce un int
, Mentre func
è dichiarato con il tipo restituito void
.Non sono sicuro che vada bene, soprattutto una volta chiamata func()
è fatto.
Soluzione
Il tuo codice ha un comportamento indefinito. Può o meno funzionare come prevedi. Il motivo per cui ha un comportamento indefinito è dovuto al 20.8.11.2.1 [func.wrap.func.con]/p7:
Richiede:
F
deve essereCopyConstructible
.f
deve essere richiamabile (20.8.11.2) per i tipi di argomentazioniArgTypes
e tipo di ritornoR
.
Per f
essere richiamabile per il tipo di ritorno R
, f
deve restituire qualcosa di implicitamente convertibile al tipo di ritorno di std::function
(void
nel tuo caso). E int
non è implicitamente convertibile in void
.
Mi aspetto che il tuo codice funzioni sulla maggior parte delle implementazioni. Tuttavia, su almeno un'implementazione (libc ++), non riesce a compilare:
test.cpp:7:30: error: no viable conversion from 'int (int)' to 'std::function<void (int)>'
std::function<void(int)> ff = f;
^ ~
Ironia della motivazione per questo comportamento deriva da un'altra domanda così.
L'altra domanda ha presentato un problema con std::function
utilizzo. La soluzione a tale problema ha comportato l'applicazione dell'implementazione Richiede: clausola al momento della compilazione. Al contrario, la soluzione al problema di questa domanda è proibitivo l'implementazione dall'applicazione del Richiede: clausola.
Altri suggerimenti
Il tuo caso d'uso è ben definito secondo lo standard.
Stai costruendo a std::function
da un oggetto richiamabile[1]
§20.8.11.2.1/7:
template<class F> function(F f);
Richiede:F deve essere CopyConstructible.f deve essere richiamabile (20.8.11.2) per i tipi di argomentazioni argtypes e tipo di ritorno R.
Quindi la tua f è chiamabile?
§20.8.11.2/2 dice:
Un oggetto Filant F del tipo F è richiamabile per i tipi di argomentazioni argtypes e il tipo di ritorno R se l'espressione
INVOKE (f, declval<ArgTypes>()..., R)
, considerato come un operando non valutato (clausola 5), è ben formata (20.8.2).
E la definizione di INVOKE
dice:
§20.8.2
Definire
INVOKE (f, t1, t2, ..., tN)
come segue:...cose che riguardano i puntatori a funzioni membro/var ...—f(t1, t2, ..., tN)
in tutti gli altri casi.Definire
INVOKE (f, t1, t2, ..., tN, R) as INVOKE (f, t1, t2, ..., tN)
implicitamente convertito inR
.
E poiché qualsiasi tipo può essere convertito implicitamente in Come sottolineato da litb di seguito, non esiste una conversione implicita in void, quindi non è ben definita.void
, il tuo codice dovrebbe andare bene con un compilatore conforme agli standard.
[1]:Penso che lambda conti come oggetto richiamabile qui, anche se non ho un riferimento per questo.Il tuo lambda potrebbe anche essere utilizzato come puntatore a funzione poiché non cattura alcun contesto
Sembra che potrebbe essere ok per le funzioni anonime.
Citazione da http://www.alorelang.org/release/0.5/doc/std_function.html (Questo non proviene dalla libreria standard C ++, tuttavia sembra che stiano usando qualcosa di simile nei legami fino a C ++)
Gli oggetti della funzione possono essere creati solo con una definizione di funzione, un'espressione della funzione anonima o accedendo a un metodo associato usando l'operatore DOT (.).
Un altro modo in cui questo potrebbe essere fatto è memorizzare il puntatore della funzione in Auto come visto qui: http://en.wikipedia.org/wiki/anonymous_function (Sezione C ++)