Was Sie wollen, kann mit Boost.Variant implementiert werden.
Die Idee ist, eine neue Art von Iterator zu definieren, die eine Variante speichert (denken Sie daran, wie eine C Vereinigung auf Steroide), die entweder eine Vorwärts- oder Rückwärts Iterator:
template<class InputRange>
struct any_dir_iterator
: std::iterator_traits<typename boost::range_iterator<InputRange>::type> {
typedef typename boost::range_iterator<InputRange>::type forward_iterator;
typedef typename
boost::range_reverse_iterator<InputRange>::type reverse_iterator;
typedef boost::variant<forward_iterator, reverse_iterator> iterator_type;
iterator_type current_it, end_it;
any_dir_iterator(InputRange & input_range,
bool fwd = true,
bool end = false)
{
end_it = fwd ? iterator_type(boost::end(input_range))
: iterator_type(boost::rend(input_range));
if(end)
current_it = end_it;
else
current_it = fwd ? iterator_type(boost::begin(input_range))
: iterator_type(boost::rbegin(input_range));
}
reference operator*() const {
return boost::apply_visitor(dereference_visitor<any_dir_iterator>(),
current_it);
}
any_dir_iterator & operator++() {
boost::apply_visitor(increment_visitor<any_dir_iterator>(),
current_it);
return *this;
}
bool operator==(any_dir_iterator const & rhs) {
return boost::apply_visitor(equals_visitor<any_dir_iterator>(),
current_it, rhs.current_it);
}
};
Dies ist ähnlich Adobe's any iterator aber viel weniger allgemein, was bedeutet, dass es im Vergleich zu einem einfachen Iterator praktisch keinen Leistungsaufwand hat.
Wie Sie oben im Code sehen können, die gesamte Logik wird auf statische Besucher delegiert, die wir wie folgt definieren:
template<class AnyDirIterator>
struct dereference_visitor
: boost::static_visitor<typename AnyDirIterator::iterator_type> {
typedef typename AnyDirIterator::reference result_type;
template<class FwdOrRevIterator>
result_type operator()(FwdOrRevIterator const & it) const {
return *it;
}
};
template<class AnyDirIterator>
struct increment_visitor
: boost::static_visitor<typename AnyDirIterator::iterator_type> {
typedef void result_type;
template<class FwdOrRevIterator>
result_type operator()(FwdOrRevIterator & it) const {
++it;
}
};
template<class AnyDirIterator>
struct equals_visitor
: boost::static_visitor<typename AnyDirIterator::iterator_type>
{
typedef bool result_type;
template <typename FwdOrRevIterator>
bool operator()(FwdOrRevIterator const & lhs,
FwdOrRevIterator const & rhs) const {
return lhs == rhs;
}
template <typename T, typename U>
bool operator()(const T &, const U &) const {
return false; // comparing fwd to rev or vice-versa
}
};
Das ist der schwierige Teil war. Aber wir müssen noch bequemer machen diese zu verwenden, für die definieren wir eine Hilfefunktion, die auf die Funktionalität beruht von der Boost.Range Bibliothek zur Verfügung gestellt:
template<class InputRange>
boost::iterator_range<any_dir_iterator<InputRange> >
make_any_dir_range(InputRange & range, bool forward=true) {
typedef any_dir_iterator<InputRange> iterator;
return boost::make_iterator_range(iterator(range, forward),
iterator(range, forward, true));
}
Und das ist alles. Jetzt können Sie schreiben:
int main() {
int items[] = { 1, 2 };
typedef std::vector<int> container_type;
container_type container(items, items + sizeof(items)/sizeof(items[0]));
BOOST_FOREACH(int i, make_any_dir_range(container, true))
std::cout << i << " ";
std::cout << "\n";
BOOST_FOREACH(int i, make_any_dir_range(container, false))
std::cout << i << " ";
return 0;
}
Welche druckt:
1 2
2 1
Das mit konst Containern funktioniert auch, obwohl ich diese Möglichkeit nicht in der main
Funktion gezeigt.
Eine weitere nette Sache, die sich aus der Verwendung von Boost.Range ergibt, ist, dass dies mit Arrays aus der Box funktioniert. So können Sie dies tun:
int items[] = { 1, 2 };
BOOST_FOREACH(int i, make_any_dir_range(items, true)) // Prints "1 2"
std::cout << i << " ";
Zu dieser Antwort kurz halten wir ein paar Dinge verlassen haben nicht umgesetzt (aber sie sind alle vorformulierten, ohne eigenen neuen Besucher):
- Der Postfix Inkrementoperator
- Der Operator =
- Die -> Operator
Hier all the code in Codepad. Aufgrund der Richtlinie "Behandle Warnungen als Fehler" wird Codepad es nicht schlucken, aber sowohl VS2008 als auch GCC 4.4 kompilieren es in meinem lokalen Rechner.
UPDATE
ich einige Tests gemacht habe und offenbar boost::variant
einig Laufzeit-Overhead nicht vorstellen: eine BOOST_FOREACH
-basierte Schleife, wie die in der main
Funktion läuft über 4-mal langsamer (wenn im Release-Modus kompiliert) als die äquivalente Version mit einem einfachen Iterator. Es wäre interessant zu prüfen, ob dies am besten oder am schlechtesten ist als der Overhead, der von Adobe any_iterator
eingeführt wurde.
Wow, das ist viel mehr als ich erwartet habe;) Ich frage mich, ob die Lösung mit einer guten 'ol Vereinigung möglich wäre. Aber ich denke schon, dass es besser ist, einen zusätzlichen Code zu haben, als diesen komplizierten Code einzuführen. Es könnte sich lohnen, wenn ich so etwas ständig mache, aber es ist nur an ein paar Orten. Danke trotzdem! –
@Alex - Ich glaube nicht, dass Gewerkschaften hier verwendet werden können, überprüfen Sie dies: http://StackOverflow.com/Questions/943267/isit-a-Good-Practice-To-use-Unions-in-C/ 943611 # 943611 – Manuel