Sono riluttante a dire che non riesco a capirlo, ma non riesco a capirlo. Ho cercato su Google e cercato Stack Overflow e sono uscito vuoto.Funzioni membro della classe istanziate da tratti [politiche, in realtà]
La forma astratta e forse eccessivamente vaga della domanda è, , come posso utilizzare il modello di tratti per creare istanze di membri?[Aggiornamento: ho usato il termine sbagliato qui. Dovrebbero essere "politiche" piuttosto che "tratti". I tratti descrivono le classi esistenti. Le politiche prescrivono le lezioni sintetiche.] La domanda è emersa mentre modernizzava una serie di ottimizzatori di funzioni multivariati che ho scritto più di 10 anni fa.
Gli ottimizzatori funzionano tutti selezionando un percorso rettilineo attraverso lo spazio dei parametri lontano dal punto migliore corrente ("aggiornamento"), quindi individuando un punto migliore su tale linea (la "ricerca di riga"), quindi eseguendo il test per la condizione "fatto", e se non fatto, iterando.
Esistono diversi metodi per eseguire l'aggiornamento, la ricerca di riga e, in teoria, per il test eseguito e altre cose. Mescolare e abbinare. Diverse formule di aggiornamento richiedono dati variabili variabili diversi. Ad esempio, l'aggiornamento LMQN richiede un vettore e l'aggiornamento BFGS richiede una matrice. Se la valutazione dei gradienti è economica, la ricerca di riga dovrebbe farlo. In caso contrario, dovrebbe utilizzare solo le valutazioni delle funzioni. Alcuni metodi richiedono ricerche di linea più accurate di altri. Questi sono solo alcuni esempi.
La versione originale crea molte delle combinazioni tramite funzioni virtuali. Alcuni tratti sono selezionati impostando i bit della modalità testati in fase di esecuzione. Che schifo. Sarebbe banale definire i tratti con # define e le funzioni membro con # ifdef e macro. Ma è così vent'anni fa. Mi infastidisce il fatto che non riesca a capire un modo moderno.
Se ci fosse un solo tratto che variava, potrei usare il modello di template che ricorre curiosamente nello . Ma non vedo alcun modo per estenderlo a combinazioni arbitrarie di tratti.
Ho provato a farlo utilizzando boost::enable_if
, ecc. Le informazioni di stato specializzate erano facili. Sono riuscito a svolgere le funzioni, ma solo ricorrendo a funzioni esterne non amichevoli che hanno come parametro il parametro this
. Non ho mai nemmeno capito come rendere le funzioni amiche, e tanto meno le funzioni membro. Il compilatore (VC++ 2008) si è sempre lamentato del fatto che le cose non corrispondevano. Io griderei: "SFINAE, idiota!" ma il deficiente è probabilmente me.
Forse la spedizione dei tag è la chiave. Non ci sono riuscito molto profondamente.
Sicuramente è possibile, giusto? In tal caso, qual è la migliore pratica?
AGGIORNAMENTO: Ecco un altro tentativo di spiegarlo. Voglio che l'utente sia in grado di compilare un ordine (manifest) per un ottimizzatore personalizzato, qualcosa come ordinare da un menu cinese - uno dalla colonna A, uno dalla colonna B, ecc. Cameriere, dalla colonna A (updaters) , Avrò l'aggiornamento BFGS con salsa Cholesky-decompositon. Dalla colonna B (line-searchers), avrò la ricerca della linea di interpolazione cubica con un eta di 0.4 e un rho di 1e-4, per favore. Etc ...
AGGIORNAMENTO: Va bene, va bene. Ecco il gioco che ho fatto. Lo offro a malincuore, perché sospetto sia un approccio completamente sbagliato. Funziona bene sotto vC++ 2008.
#include <boost/utility.hpp>
#include <boost/type_traits/integral_constant.hpp>
namespace dj {
struct CBFGS {
void bar() {printf("CBFGS::bar %d\n", data);}
CBFGS(): data(1234){}
int data;
};
template<class T>
struct is_CBFGS: boost::false_type{};
template<>
struct is_CBFGS<CBFGS>: boost::true_type{};
struct LMQN {LMQN(): data(54.321){}
void bar() {printf("LMQN::bar %lf\n", data);}
double data;
};
template<class T>
struct is_LMQN: boost::false_type{};
template<>
struct is_LMQN<LMQN> : boost::true_type{};
// "Order form"
struct default_optimizer_traits {
typedef CBFGS update_type; // Selection from column A - updaters
};
template<class traits> class Optimizer;
template<class traits>
void foo(typename boost::enable_if<is_LMQN<typename traits::update_type>,
Optimizer<traits> >::type& self)
{
printf(" LMQN %lf\n", self.data);
}
template<class traits>
void foo(typename boost::enable_if<is_CBFGS<typename traits::update_type>,
Optimizer<traits> >::type& self)
{
printf("CBFGS %d\n", self.data);
}
template<class traits = default_optimizer_traits>
class Optimizer{
friend typename traits::update_type;
//friend void dj::foo<traits>(typename Optimizer<traits> & self); // How?
public:
//void foo(void); // How???
void foo() {
dj::foo<traits>(*this);
}
void bar() {
data.bar();
}
//protected: // How?
typedef typename traits::update_type update_type;
update_type data;
};
} // namespace dj
int main() {
dj::Optimizer<> opt;
opt.foo();
opt.bar();
std::getchar();
return 0;
}
Puoi mostrare un esempio di cosa stai facendo ora in pseudo codice, forse? –
@Chris Kaminski: Il codice di 10 anni o gli esperimenti? Quest'ultimo, presumo. –
Forse entrambi? Voglio dire, ci sono molti esempi nel STL e aumentano l'uso delle funzioni che operano su collezioni, ad esempio mappa/riduci. –