2009-07-06 9 views
6

Chcę napisać funkcję, która wyprowadza coś do ostream która jest przekazywana w, i powrócić do strumienia, jak poniżej:Funkcja, która drukuje coś na std :: ostream i zwraca std :: ostream?

std::ostream& MyPrint(int val, std::ostream* out) { 
    *out << val; 
    return *out; 
} 

int main(int argc, char** argv){ 
    std::cout << "Value: " << MyPrint(12, &std::cout) << std::endl; 
    return 0; 
} 

byłoby wygodne, aby wydrukować wartość takiego i osadzić wywołanie funkcji w łańcuch wyjściowy operatora, tak jak w przypadku main().

To nie działa, jednak i drukuje:

$ ./a.out 
12Value: 0x6013a8 

pożądany wynik byłby ten:

Value: 12 

Jak mogę rozwiązać ten problem? Czy muszę zamiast tego zdefiniować operator<<?

AKTUALIZACJA: Wyjaśniono, jaka będzie żądana moc wyjściowa.

UPDATE2: Niektórzy ludzie nie rozumieli, dlaczego wydrukowałbym taką liczbę, używając funkcji zamiast bezpośredniego drukowania. Jest to uproszczony przykład, w rzeczywistości funkcja wypisuje obiekt złożony, a nie int.

+1

Dlaczego przekazujesz strumień przez wskaźnik, a nie za pośrednictwem kryterium? –

+0

Może czytał przewodnik w stylu Google i kupił ten styl mody "nie używaj niestacjonarnych parametrów odniesienia". –

+0

Ponadto, o co chodzi z tym, że nie robimy "cout <<" Wartość: "<< 12;' jak @Neil zaleca? To wydaje się najbardziej proste rozwiązanie. –

Odpowiedz

11

Nie można naprawić funkcji. Nic w specyfikacji nie wymaga kompilatora do oceny wywołania funkcji w wyrażeniu w dowolnej kolejności w odniesieniu do niektórych niepowiązanych operatorów w tym samym wyrażeniu. Więc bez zmiany kodu wywołującego, nie można dokonać MyPrint() oceniać po std::cout << "Value: "

lewej do prawej zamówienie jest upoważniona do wyrażenia składające się z wielu kolejnych < < operatorów, tak, że będzie działać. Punktem zwrotnym operatora jest zwracanie strumienia, gdy operatory są powiązane, LHS każdego z nich jest dostarczane przez ocenę operatora po lewej stronie.

Nie można osiągnąć tego samego z bezpłatnymi wywołaniami funkcji, ponieważ nie mają one LHS.MyPrint() zwraca obiekt równy std::cout, podobnie jak std::cout << "Value: ", więc skutecznie robisz std::cout << std::cout, który drukuje tę wartość szesnastkową.

Ponieważ sygnał wyjściowy jest:

Value: 12 

"prawo" rzeczą do zrobienia jest rzeczywiście zastąpić operatorowi < <. To często oznacza, że ​​trzeba albo zrobić z niego przyjaciela, czy to zrobić:

class WhateverItIsYouReallyWantToPrint { 
    public: 
    void print(ostream &out) const { 
     // do whatever 
    } 
}; 

ostream &operator<<(ostream &out, const WhateverItIsYouReallyWantToPrint &obj) { 
    obj.print(out); 
} 

Jeśli nadrzędnym operator<< dla swojej klasie nie jest właściwe, na przykład dlatego, że istnieje wiele formatów, które mogą chcesz wydrukować, a ty chcesz napisać inną funkcję dla każdego, wtedy powinieneś albo zrezygnować z pomysłu na łańcuchy operatorów i po prostu wywołać funkcję, albo napisać wiele klas, które biorą twój obiekt jako parametr konstruktora, każdy z innym przeciążeniem operatora.

+0

Wiem, że zawsze łatwiej jest rozbić strumienie na tradycyjne wywołania funkcji, gdy mogę nie rozumiem czegoś, więc pomyślałem, że dodam to do OP, gdyby im to pomogło.) Mam nadzieję, że nie masz nic przeciwko, myślę, że mam rację mówiąc, że linia std :: cout zasadniczo sprowadza się do cout.operator << ("Wartość:") .operator << (MyPrint (12, & std :: cout)) .operator << (std :: endl); co może być łatwiejszym sposobem wizualizacji ta odpowiedź mówi: – Troubadour

+0

Z pewnością nie mam nic przeciwko: dzięki! –

+0

Uaktualniłem pytanie, teraz mówi, że pożądana wartość wyjściowa będzie Wartość: 12. – Frank

5

Chcesz mieć klasę myprint z przyjacielem operatora < <:

class MyPrint 
{ 
public: 
    MyPrint(int val) : val_(val) {} 
    friend std::ostream& operator<<(std::ostream& os, const MyPrint& mp) 
    { 
     os << mp.val_; 
     return os; 
    } 
private: 
    int val_; 
}; 

int main(int argc, char** argv) 
{ 
    std::cout << "Value: " << MyPrint(12) << std::endl; 
    return 0; 
} 

Ta metoda wymaga, aby wstawić obiekt do strumienia myprint swojego wyboru. Jeśli naprawdę potrzebujesz możliwość zmiany, które strumień jest aktywny, można to zrobić:

class MyPrint 
{ 
public: 
    MyPrint(int val, std::ostream& os) : val_(val), os_(os) {} 
    friend std::ostream& operator<<(std::ostream& dummy, const MyPrint& mp) 
    { 
     mp.os_ << mp.val_; 
     return os_; 
    } 
private: 
    int val_; 
    std::ostream& os_ 
}; 

int main(int argc, char** argv) 
{ 
    std::cout << "Value: " << MyPrint(12, std::cout) << std::endl; 
    return 0; 
} 
+0

Tyle tylko, że to nie robi tego, o co prosiło. –

+0

Jak to zrobić? Proszę wytłumacz. – rlbond

+1

Na początek nie kompiluje się. –

0

Po pierwsze, nie ma powodu, aby nie przechodzić w ostream poprzez odniesienie raczej niż przez wskaźnik:

std::ostream& MyPrint(int val, std::ostream& out) { 
    out << val; 
    return out; 
} 

Jeśli naprawdę nie chcesz używać std::ostream& operator<<(std::ostream& os, TYPE), można to zrobić:

int main(int argc, char** argv){ 
    std::cout << "Value: "; 
    MyPrint(12, std::cout) << std::endl; 
    return 0; 
} 
+0

"nie ma powodu, aby nie podawać ostream przez odniesienie". Niektórzy ludzie nie lubią stałych parametrów odniesienia. Nie zgadzam się z nimi, ale nie powiedziałbym, że nie ma "żadnego powodu" dla ich preferencji. –

+0

Nie widzę, jak wskaźnik niestały byłby bardziej pożądany niż odniesienie do stałych w dowolnym normie kodowania. –

+0

Google mówi: "Referencje mogą być mylące, ponieważ mają składnię wartości, ale mają znaczenie semantyczne" (http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml?showone=Reference_Arguments#Reference_Arguments). Sądzę, że mają na myśli to, że wartość pass-by-value i pass-by-reference wyglądają tak samo na stronie wywołania, więc uważają, że kod jest bardziej czytelny, jeśli czytelnicy mogą założyć, że wszystko, co wygląda jak wartość przekazana, pozostawia wartość niemodyfikowany. I jak to się mówi, Google jest szalony jak lis. Ma trochę staromodne podejście do C++, ale myślę, że nie jest irracjonalny. –

-1

w Twoim przypadku odpowiedź brzmi oczywiście:

std::cout << "Value: " << 12 << std::endl; 

Jeśli to nie wystarczy, proszę wyjaśnić, jakie dane wyjściowe mają zostać wyświetlone.

+1

Przykład jest oczywiście uproszczony i pokazuje, że użytkownik chce zrozumieć, w jaki sposób prowadzić łańcuch operacji strumieniowych. –

+2

W przeciwieństwie do wielu osób w tym miejscu, nie mam łatwego sposobu na zrozumienie "oczywistego" niezaplanowanego pytania - lubię mieć rzeczy opisane. –

+0

Myślę, że powinieneś wyłowić swoją ikonę do zrzędliwej twarzy rozgwiazda :-( –

1

Nie ma sposobu, aby robić to, czego spodziewaliśmy się tam ze względu na celu funkcje są oceniane.

Czy jest jakiś szczególny powód, dla którego trzeba napisać bezpośrednio do ostream takiego? Jeśli nie, wystarczy, że MyPrint zwróci ciąg znaków. Jeśli chcesz użyć strumienia w MyPrint, aby wygenerować wynik, po prostu użyj strumienia i zwróć wynik.

3

Masz dwie opcje. Pierwszy z nich, za pomocą tego, co już jest:

std::cout << "Value: "; 
MyPrint(12, &std::cout); 
std::cout << std::endl; 

Drugi, który jest bardziej C++ - jak, jest zastąpienie MyPrint() z odpowiednim std::ostream& operator<<. Jest już jeden dla int, więc zrobię jedną tylko odrobinę bardziej złożona:

#include <iostream> 

struct X { 
    int y; 
}; 

// I'm not bothering passing X as a reference, because it's a 
// small object 
std::ostream& operator<<(std::ostream& os, const X x) 
{ 
    return os << x.y; 
} 

int main() 
{ 
    X x; 
    x.y = 5; 
    std::cout << x << std::endl; 
} 
0

Po zmianie wskaźnik odniesienia, można to zrobić:

#include <iostream> 

std::ostream& MyPrint(int val, std::ostream& out) { 
    out << val; 
    return out; 
} 

int main(int, char**) { 
    MyPrint(11, std::cout << "Value: ") << std::endl; 

    return 0; 
} 

Składnia MyPrint jest zasadniczo rozwinięty operator<<, ale z dodatkowym argumentem.

Powiązane problemy