Il problema è che il compilatore è ottimizzato, nella prima chiamata, quella a g() quello che ritorna come oggetto temporaneo anonimo (return a) è utilizzato per inizializzare foo1.

Per foo2 tutto ok, l'hai capito anche tu.

Per foo3 se tu al momento della dichiarazione assegni qualcosa usa il costruttore e non l'assegnazione.
Cioè:
foo foo3 = foo1;
è uguale a scrivere:
foo foo3(foo1);