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 *this false; }
} 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