2013-12-14 19 views
7

Chcę użyć opartego na odległościach for do iteracji przez punkty kodu Unicode w kodowanej UTF8 std::string. W globalnym obszarze nazw zdefiniowałem własne wartości begin i end, ale preferowane są obszary nazw begin i end w przestrzeni nazw std (tj. Te znalezione przez ADL). Czy istnieje sposób na preferowanie moich własnych funkcji?Preferuj niektóre funkcje w stosunku do ADL

Przykład:

const char* begin(const std::string& s) { 
    std::cout << "BEGIN"; 
    return s.data(); 
} 

const char* end(const std::string& s) { 
    std::cout << "END"; 
    return s.data() + s.length(); 
} 

int main() { 
    std::string s = "asdf"; 

    for (char c : s) 
     std::cout << c; 
} 

chcę go wydrukować BEGINENDasdf (lub ENDBEGINasdf), ale drukuje asdf.

Czy nie ma innej możliwości niż ręczne używanie for przy użyciu kwalifikowanej nazwy?

+0

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) –

+3

@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

+0

@ uk4321 Możesz być zainteresowany [n3257] (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3257.pdf). –

Odpowiedz

6

Wrap std::string w swoim własnym typie. Dzięki temu szablonowi można dostosować dowolny istniejący kontener i dodać do niego swoją własną logikę zasięgu. To nawet nie różni się od twojej pierwszej próby.

#include <string> 
#include <iostream> 

template <typename S> 
struct custom_container { 
    S &s_; 

    custom_container (S &s) : s_(s) {} 

    auto begin() -> decltype(s_.begin()) { 
     std::cout << "BEGIN"; 
     return s_.begin(); 
    } 

    auto end() -> decltype(s_.end()) { 
     std::cout << "END"; 
     return s_.end(); 
    } 
}; 

template <typename S> 
custom_container make_container (S &s) { 
    return custom_container <S> (s); 
} 


int main() { 
    std::string t = "asdf"; 
    auto s = make_container(t); 

    for (char c : s) { 
     std::cout << c; 
    } 
} 

Wyjścia

BEGINENDasdf

6

N3337 6.5.4/1:

(...) rozpocząć-expr i końca wyraż są określane w następujący sposób:

- jeśli _RangeT jest typu tablicą rozpocząć lista_wyrażeń_sortowania i końcową lista_wyrażeń_sortowania__range i __range + __bound, odpowiednio, (...);

- jeśli _RangeT to typ klasy, Bez zastrzeżeń Internet red ID s begin i end są szuka się w zakresie klasy _RangeT jakby członkiem klasy dostępu odnośnika (3.4.5), jeśli albo (lub oba) NDS fi co najmniej jedno zgłoszenie, rozpocząć lista_wyrażeń_sortowania i końcową lista_wyrażeń_sortowania__range.begin() i __range.end(), odpowiednio;

- inaczej rozpocząć lista_wyrażeń_sortowania i końcową lista_wyrażeń_sortowaniabegin(__range) i end(__range), odpowiednio, gdzie begin i end są wyszukiwane z argumentu zależne od wyszukiwarki (3.4.2). Dla potrzeb tej nazwy odnośnik , przestrzeń nazw std jest skojarzoną przestrzenią nazw.

Więc innymi słowy, będzie to nazwać begin i end funkcji składowych std::string „s (druga lista punkt). Prawidłowym rozwiązaniem jest dostarczenie klasy opakowania, jak sugeruje odpowiedź anthony.

Uwaga: jeśli używasz -std=c++1y możesz pominąć końcowy typ decltype.

Można również napisać typedef aby uczynić go mniej wpisując:

typedef custom_string<std::string> cs; 

for (char c : cs(t)) { 
    std::cout << c; 
} 
1

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