Data una funzione f(x, y, z)
è possibile associare x
a 0, ottenendo una funzione g(y, z) == f(0, y, z)
. Possiamo continuare a farlo e ottenere h() = f(0, 1, 2)
.Problemi con l'applicazione std :: binding ricorsivo su una std :: funzione
In C++ sintassi che sarebbe
#include <functional>
#include <iostream>
void foo(int a, long b, short c)
{
std::cout << a << b << c << std::endl;
}
int main()
{
std::function<void(int, long, short)> bar1 = foo;
std::function<void(long, short)> bar2 = std::bind(bar1, 0, std::placeholders::_1, std::placeholders::_2);
std::function<void(short)> bar3 = std::bind(bar2, 1, std::placeholders::_1);
std::function<void()> bar4 = std::bind(bar3, 2);
bar4(); // prints "012"
return 0;
}
Fin qui tutto bene.
Ora dire che voglio fare lo stesso: associare il primo argomento di una funzione, richiamare la nuova funzione e ripetere questo processo finché tutti gli argomenti non sono vincolati, ma generalizzarlo per funzionare non solo con una funzione di 3 argomenti come nell'esempio C++ sopra, ma con una funzione con numero sconosciuto * di argomenti.
* In C++ esistono argomenti variadici e in C++ 11 ci sono modelli variadici. Mi riferisco ai modelli variadici qui.
Fondamentalmente, ciò che voglio essere in grado di fare, è scrivere una funzione che accetta qualsiasi std::function
e ricorsivamente associa il primo argomento a qualche valore finché tutti gli argomenti non sono vincolati e la funzione può essere chiamata.
Per la semplicità, supponiamo che std::function
rappresenti una funzione che accetta qualsiasi argomento integral e restituisce vuoto.
Questo codice può essere considerate come una generalizzazione del codice precedente
#include <functional>
#include <iostream>
// terminating case of recursion
void apply(std::function<void()> fun, int i)
{
fun();
}
template<class Head, class... Tail>
void apply(std::function<void(Head, Tail...)> f, int i)
{
std::function<void(Tail...)> g = std::bind(f, i);
apply<Tail...>(g, ++i);
}
void foo(int a, long b, short c)
{
std::cout << a << b << c << std::endl;
}
int main()
{
std::function<void(int, long, short)> bar1 = foo;
apply<int, long, short>(bar1, 0);
return 0;
}
Questo codice è grande. È esattamente quello che voglio Non viene compilato.
main.cpp: In instantiation of 'void apply(std::function<void(Head, Tail ...)>, int) [with Head = int; Tail = {long int, short int}]':
main.cpp:24:40: required from here
main.cpp:12:56: error: conversion from 'std::_Bind_helper<false, std::function<void(int, long int, short int)>&, int&>::type {aka std::_Bind<std::function<void(int, long int, short int)>(int)>}' to non-scalar type 'std::function<void(long int, short int)>' requested
std::function<void(Tail...)> g = std::bind(f, i);
^
Il problema è che non si può semplicemente lasciare fuori std::placeholders
in std::bind
chiamata così. Sono obbligatori e il numero di segnaposto in std::bind
deve corrispondere al numero di argomenti non vincolati nella funzione.
Se cambiamo linea
std::function<void(Tail...)> g = std::bind(f, i);
a
std::function<void(Tail...)> g = std::bind(f, i, std::placeholders::_1, std::placeholders::_2);
vediamo che passa con successo attraverso il primo apply()
chiamata, ma si blocca al secondo passaggio, perché durante il secondo passaggio g
esigenze solo un segnaposto, mentre ne abbiamo ancora due nello std::bind
.
main.cpp: In instantiation of 'void apply(std::function<void(Head, Tail ...)>, int) [with Head = long int; Tail = {short int}]':
main.cpp:13:30: required from 'void apply(std::function<void(Head, Tail ...)>, int) [with Head = int; Tail = {long int, short int}]'
main.cpp:24:40: required from here
main.cpp:12:102: error: conversion from 'std::_Bind_helper<false, std::function<void(long int, short int)>&, int&, const std::_Placeholder<1>&, const std::_Placeholder<2>&>::type {aka std::_Bind<std::function<void(long int, short int)>(int, std::_Placeholder<1>, std::_Placeholder<2>)>}' to non-scalar type 'std::function<void(short int)>' requested
std::function<void(Tail...)> g = std::bind(f, i, std::placeholders::_1, std::placeholders::_2);
^
C'è un modo per risolvere questo utilizzando i modelli non variadic regolari, ma introduce un limite al numero di argomenti std::function
può avere. Ad esempio, questo codice funziona solo se std::function
ha 3 o meno argomenti
(sostituire apply
funzioni nel codice precedente su questi)
// terminating case
void apply(std::function<void()> fun, int i)
{
fun();
}
template<class T0>
void apply(std::function<void(T0)> f, int i)
{
std::function<void()> g = std::bind(f, i);
apply(g, ++i);
}
template<class T0, class T1>
void apply(std::function<void(T0, T1)> f, int i)
{
std::function<void(T1)> g = std::bind(f, i, std::placeholders::_1);
apply<T1>(g, ++i);
}
template<class T0, class T1, class T2>
void apply(std::function<void(T0, T1, T2)> f, int i)
{
std::function<void(T1, T2)> g = std::bind(f, i, std::placeholders::_1, std::placeholders::_2);
apply<T1, T2>(g, ++i);
}
Ma il problema con tale codice è che avrei definire un nuova funzione apply
per supportare std::function
con 4 argomenti, quindi la stessa cosa con 5 argomenti, 6 e così via.Senza contare che il mio obiettivo era di non avere limiti hard-coded sul numero di argomenti. Quindi questo non è accettabile. Non voglio che abbia un limite.
Ho bisogno di trovare un modo per far funzionare il codice del modello variadic (il secondo frammento di codice).
Se solo std::bind
non ha richiesto di specificare i segnaposto - tutto avrebbe funzionato, ma come std::bind
opere attualmente, abbiamo bisogno di trovare un modo per specificare il giusto numero di segnaposto.
Potrebbe essere utile sapere che siamo in grado di trovare il giusto numero di segnaposto per specificare con C++ 11 del sizeof...
sizeof...(Tail)
ma non ho potuto ottenere qualcosa di buono da questo fatto.
Il mio primo consiglio è di smettere di usare 'std :: bind' a meno che non sia assolutamente necessario. – Yakk
C++ ora è C++ 11. – Puppy
@Puppy Non consideri nemmeno C++ 14? –