2010-08-07 9 views
11

tuple w podbiciu i TR1/C++ 0x zapewnia wygodną (dla pisarza funkcji) metodę zwracania dwóch wartości z funkcji - jednak wydaje się, że uszkodzenie jednego główną cechą języka do rozmówcy: zdolność do po prostu użyć funkcji zainicjować zmienną:Sposób inicjalizacji z wieloma wartościami zwracanymi w C++ (0x)

T happy(); 
const auto meaningful_name(happy()); // RVO means no excess copies 

ale dla:

tuple<T,U> sad(); 

nie mamy się poddać możliwość wybrać znacząca nazwa dla naszych wartości zwracanych i użyj get<n>() wszędzie:

const auto two_unrelated_things(sad()); 

lub dokonać tymczasowej:

const auto unwanted_named_temporary(sad()); 
const auto one_name(get<0>(unwanted_named_temporary)); 
const auto two_name(get<1>(unwanted_named_temporary)); 

lub przełączyć się z inicjalizacji do cesji, która działa tylko wtedy, gdy typy są przypisane, i łamie auto:

tuple_element<0, decltype(sad())>::type one_mutable; // there might be a less 
tuple_element<1, decltype(sad())>::type two_mutable; // verbose way 
tie(one_mutable,two_mutable) = sad(); 

lub zrób coś nienaturalnego dla lokalnej klasy:

const struct ugh { 
    ugh(decltype(sad()) rhs) : one_name(get<0>(rhs)), two_name(get<1>(rhs)) {} 
    const tuple_element<0, decltype(sad())>::type one_name; 
    const tuple_element<1, decltype(sad())>::type two_name; 
} stuff(sad()); // at least we avoid the temporary and get initialization 

Czy istnieje lepszy sposób? Używam powyższych konstruktów kompatybilnych z VC10, czy cokolwiek w pełnym C++ 0x lub wspomaganie?

Idealnie byłoby:

  • pozwala mi używać inicjalizacji nie tylko zadanie
  • niech rozmówca wybierać imiona dla zwróconych-do zmiennych
  • nie wprowadzać dodatkowych kopii
  • działa zarówno dla zmiennych stosowych, jak i dla członków klasy:
  • może być dużą biblioteką szalonych szablonów, ale ma rozsądną składnię dla pisarza wywołującego i funkcji
+2

Interesujące pytanie, chociaż nie widzę sposobu, w jaki można zdefiniować zmienne różnych typów w pojedynczym wyrażeniu. - Myślę, że opcja "lub uczyń tymczasową" może być OK, jeśli zmienione zmienne zostaną zmienione na odniesienia (unikanie kopiowania). – UncleBens

+0

Dobra uwaga odnośnie referencji - myślę, że to jest rozwiązanie dla zmiennych stosu. Próbowałem robić to samo w klasie: class c { publicznego: C (O) SR (SAD()), jeden (dostać <0> (SR)), dwa (dostać <1> (SR)) {} const T & one; const U & two; interent: tuple sr; } Ale wygląda na to, że w VC10, C jest o dwa wskaźniki większe od krotki, a nie wielka sprawa, ale rodzaj kulawy - czy kompilator nie uznałby legalności za odwołania i alokowanie przestrzeni w przypadku dla nich? Czy to właśnie dlatego wskaźniki do referencji są w pierwszej kolejności nielegalne? – BCoates

+0

W przypadku klasy, jeśli dane są przechowywane jako krotka, można po prostu podać nazwane metody dostępu, które wywołują odpowiednie 'get '. Wątpię, czy pojawi się rozwiązanie oparte na "szalonym szablonie", ponieważ podstawowy język po prostu nie wspiera tego, o co prosisz. Być może po prostu zmniejszysz liczbę znaków, które musisz wpisać za pomocą makr ... – UncleBens

Odpowiedz

2
std::tuple<Type1, Type2> returnValue = sad(); 
Type1& first = std::get<0>(returnValue); 
Type2& second = std::get<1>(returnValue); 

Nie jestem pewien, co się czwarte środki kulach, ale spełnia cała reszta.

* edytuj: Na podstawie powyższego komentarza dowiedziałem się, co miał na myśli czwarty punktor.

struct Object { 
    Object(const std::tuple<Type1, Type2>& t) : value(t) { } 
    Type1& First() { return std::get<0>(value); } 
    Type2& second() { return std::get<1>(value); } 
private: 
    std::tuple<Type1, Type2> value; 
} 

Zmień w razie potrzeby.

W ogóle nie można po prostu użyć wartości std::tuple, jeśli zwrócone wartości są tak niezwiązane, że trzeba je rozdzielić, aby mogły być używane w rozsądny sposób. Ludzie dostali od lat zwrócone struct s z polami o rozsądnie określonej nazwie lub akceptując parametry referencyjne dla danych wyjściowych.

Na marginesie wydaje się, że jesteś zakochany w auto. Nie bądź. Jest to świetna funkcja, ale jest to , a nie sposób, w jaki powinien być używany. Twój kod będzie nieczytelny, jeśli nie określisz typów od czasu do czasu.

+0

Jak myślę o "członku klasy", czy jest to nawet sytuacja, w której można usunąć wszystkie kopie? Sądzę, że przynajmniej w jednym przypadku skończysz kopiowanie "wartości", co oczywiście będzie wymagało co najmniej jednego kopiowania jej członków. Więc prawdopodobnie równie dobrze możesz przechowywać wartości bezpośrednio i zapomnieć o przechowywaniu krotki w ogóle. –

+0

Zgadzam się, że nie używam 'std :: tuple', gdy nie jest to właściwe - obawiam się, że stanie się on idiomatyczny i uczyni kod mniej zrozumiałym i pełnym' get (onceup) ' To powiedziane , wygląda na to, że martwię się zbytnio o dodatkowe kopie: 'tie (a, b) = sad()' w konstruktorze skutecznie eloduje wszystkie kopie krotek, gdy sad() 'zwraca make_tuple (...)' . Może lekcja jest "" const "członkowie danych są kłopotów". – BCoates

+1

@bcoates: w odniesieniu do niewłaściwego i nadużywania krotki. Biblioteka standardowa sama jest trochę winna, gdy przedstawia zakresy jako 'pair '. OTOH, boost używa wyższej alternatywy: 'iterator_range ' z 'begin()' i 'end()' method. Zakres nie jest abstrakcyjną parą, "pierwszy" i "drugi" mają konkretne znaczenie! - Nie sądzę, że w kodzie nietypowym powinno się bardzo kręcić. – UncleBens

Powiązane problemy