risorse | operatori infissi in c++
In questo periodo al quotidiano lavoro in C++ si è affiancato qualche piccolo progetto Python. Ho assimilato la sintassi di questo linguaggio a tal punto che spesso e volentieri mi capita di “pythonizzare” il mio C++: oltre a dimenticarmi sistematicamente le parentesi graffe, mi capita per esempio di scrivere:
int i = 3; std::vector<int> numbers { 1, 2, 3, 4, 5, }; ... if (i in numbers) ...
Mi sono così chiesto se esiste il modo di rendere compilabile codice come questo.
Il codice di partenza è il seguente:
#include <iostream> #include <vector> int main() { std::vector<int> numbers { 1, 2, 3, 4, 5, }; int i = 3; if (i in numbers) // compiler error! std::cout << i << " found\n"; else std::cout << i << " not found\n"; }
Una forma sintattica non troppo difforme da quella desiderata che con poca fatica si può rendere compilabile è la seguente:
if (i +in+ numbers)
Supponendo che in sia un oggetto di un tipo opportuno, e tenendo conto dell'associatività dell'operatore +, il valore dell'espressione viene determinato in tre passi:
Affinché tale espressione sia compilabile, è sufficiente che l'oggetto in supporti:
Ecco come si presenta il codice dopo le modifiche suggerite:
#include <iostream> #include <vector> struct in_operator { operator bool() const { return false; } template <class T> in_operator& operator+(const T&) { return *this; } } in; template <class T> in_operator& operator+(const T&, in_operator& op) { return op; } int main() { std::vector<int> numbers { 1, 2, 3, 4, 5, }; int i = 3; if (i +in+ numbers) std::cout << i << " found\n"; else std::cout << i << " not found\n"; } /* output: * * 3 not found */
È evidente che il programma non funziona, però compila!
Si può evitare la conversione implicita a bool se si ha l'accortezza di ritornare il booleano atteso direttamente dall'operator+ membro:
#include <iostream> #include <vector> struct in_operator {operator bool() const { return false; }template <class T>in_operator&bool operator+(const T&) { return*thisfalse; } } in; template <class T> in_operator& operator+(const T&, in_operator& op) { return op; } int main() { std::vector<int> numbers { 1, 2, 3, 4, 5, }; int i = 3; if (i +in+ numbers) std::cout << i << " found\n"; else std::cout << i << " not found\n"; } /* output: * * 3 not found */
Poiché è necessario conoscere il valore del primo termine nel contesto del secondo operator+ (onde ritornare il valore di verità corretto), è indispensabile “catturarlo”, e il modo più semplice per farlo è inglobarlo in un oggetto ausiliario ad hoc nella prima chiamata a operator+, dov'è disponibile una sua referenza:
#include <iostream> #include <vector> #include <algorithm> struct in_operator {template <class T> bool operator+(const T&) { return false; }} in; template <class T> struct in_operator_impl { const T& lhs_; in_operator_impl(const T& lhs) : lhs_(lhs) {} template <class Sequence> bool operator+(const Sequence& s) { return std::find(std::begin(s), std::end(s), lhs_) != std::end(s); } };template <class T> in_operator& operator+(const T&, in_operator& op) { return op; }template <class T> in_operator_impl<T> operator+(const T& lhs, in_operator&) { return lhs; } int main() { std::vector<int> numbers { 1, 2, 3, 4, 5, }; int i = 3; if (i +in+ numbers) std::cout << i << " found\n"; else std::cout << i << " not found\n"; } /* output: * * 3 found */
Il programma ora funziona: l'espressione i + in dà origine ad un oggetto temporaneo di tipo in_operator_impl<int> il quale, attraverso l'applicazione dell'operator+ membro invocato durante la valutazione della sotto-espressione x + numbers, fornisce il risultato booleano atteso.
Ci sono almeno due aspetti discutibili nell'implementazione: la presenza dell'oggetto statico/globale in e la sintassi dell'operatore, che non è ancora nella forma desiderata. Entrambi i problemi sono però facilmente risolvibili introducendo una macro che si occupa di inserire i due caratteri + e che sostituisce l'oggetto statico con uno temporaneo:
#include <iostream> #include <vector> #include <algorithm> struct in_operator {}in; template <class T> struct in_operator_impl { const T& lhs_; in_operator_impl(const T& lhs) : lhs_(lhs) {} template <class Sequence> bool operator+(const Sequence& s) { return std::find(std::begin(s), std::end(s), lhs_) != std::end(s); } }; template <class T>in_operator_impl<T> operator+(const T& lhs, in_operator&) {in_operator_impl<T> operator+(const T& lhs, in_operator&&) { return lhs; } #define in +in_operator()+ int main() { std::vector<int> numbers { 1, 2, 3, 4, 5, }; int i = 3;if (i +in+ numbers)if (i in numbers) std::cout << i << " found\n"; else std::cout << i << " not found\n"; int j = 7; if (j in numbers) std::cout << j << " found\n"; else std::cout << j << " not found\n"; } /* output: * * 3 found * 7 not found */
Pagina modificata il 26/06/2015