najczystszy sposób to zrobić, przynajmniej w momencie użycia, jest to, aby zaznaczyć swój typ na potrzeby specjalnego iteracji .
Po pierwsze, niektóre maszyny:
template<class Mark, class T>
struct marked_type {
T raw;
marked_type(T&& in):raw(std::forward<T>(in)) {}
};
template<typename Mark, typename T>
marked_type<Mark, T> mark_type(T&& t) {
return {std::forward<T>(t)};
}
obok, wymyślamy znak, który mówi "iteracyjne dziwnie", a przeciążenie rozpoczęcia/zakończenia:
struct strange_iteration {};
template<typename T>
auto begin(marked_type<strange_iteration, T> const& container)
-> decltype(std::begin(std::forward<T>(container.raw)))
{
std::cout << "BEGIN";
using std::begin;
return begin(std::forward<T>(container.raw));
}
template<typename T>
auto end(marked_type<strange_iteration, T> const& container)
-> decltype(std::end(std::forward<T>(container.raw)))
{
std::cout << "END";
using std::end;
return end(std::forward<T>(container.raw));
}
a następnie w momencie użycia:
std::string s = "hello world";
for(char c : mark_type<strange_iteration>(s)) {
std::cout << c;
}
std::cout << "\n";
z jedną nutą, którą napisałem mark_type
, aby była zbyt ogólna.
Teraz mark_type<Foo>
utworzy odniesienia do l-wartości i utworzy przeniesioną kopię wartości rwartości, jeśli zostanie do niej przekazana. W iteracji czas życia jego wartości powracającej zostanie przedłużony przez odniesienie do czasu życia.
Możesz użyć tej techniki do robienia rzeczy, jak
for(char c : mark_type<reverse_iteration>(s))
gdzie teraz zamiast iteracji wstecznej, niezależnie od pojemnika przeszliśmy do środka. „Tworzenie kopii” na rvalue jest potrzebne do konstrukcji takich jak to:
for(char c: mark_type<reverse_iteration>(mark_type<strange_iteration>(s))
gdzie prowadzimy łańcuch znaków. Lifetime extension odnosi się tylko do zewnętrznej wartości zwracanej, a nasze "create a copy and move" na rvalue to w zasadzie ręczne przedłużenie życia.
Na koniec, użycie w powyższym kodzie std::begin
lepiej wykonać w kontekście przyjmującym ADL w wartościach zwracanych. Tworzenie nazw pomocnika takiego:
namespace adl_helper {
using std::begin; using std::end;
template<typename T>
auto adl_begin(T&& t)->decltype(begin(std::forward<T>(t))); // no implementation
template<typename T>
auto adl_end(T&& t)->decltype(end(std::forward<T>(t))); // no implementation
// add adl_cbegin, adl_rbegin etc in C++14
}
następnie zastąpić std::begin
w decltype
jest w moim powyższym kodem z adl_helper::adl_begin
, który emuluje jak for(a:b)
pętle znaleźć begin
i end
dotyk lepiej (nie idealnie, ale lepiej).
C++ 1y może pochodzić z niektórych urządzeń, aby usunąć potrzebę powyższego hack.
kod
Próbka trwania: http://ideone.com/RYvzD0
Uwaga: baza założenie jest błędne: nie chodzi tu ADL, '' s' jest std :: string' więc 'dla (char c: s)' będzie zachowywać tak jak przy użyciu _member_ form 's.begin()' i 's.end()', nie jest to forma non-member 'begin (s)' i 'end (s)' (patrz odpowiedź remyabel) –
@gx_ yes , niektóre z poniższych odpowiedzi wspomniały o tym. Odwróciłem się i pomyślałem, że próba "nie-członka" została podjęta jako pierwsza, a człon "zaczyna" od drugiej. – uk4321
@ uk4321 Możesz być zainteresowany [n3257] (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3257.pdf). –