2013-07-14 21 views
21

Czy istnieje sposób napisania funkcji w C++, która akceptuje zarówno argumenty lvalue, jak i rvalue, bez tworzenia szablonu?Funkcja akceptująca oba argumenty: lvalue i rvalue

Załóżmy na przykład, że napisałem funkcję print_stream, która czyta z istream i drukuje dane, które zostały odczytane na ekranie lub coś podobnego.

Myślę, że to rozsądne, aby zadzwonić print_stream takiego:

fstream file{"filename"}; 
print_stream(file); 

jak również tak:

print_stream(fstream{"filename"}); 

Ale jak mogę zadeklarować print_stream tak, że oba zastosowania działa?

Jeśli deklarują jako

void print_stream(istream& is); 

wówczas drugi zastosowanie nie będzie opracować ponieważ RValue nie będą wiązać się z const odniesieniu lwartości.

Jeśli deklarują jako

void print_stream(istream&& is); 

czym pierwsze użycie nie kompilacji ponieważ lwartością nie będą wiązać się z odniesieniem RValue.

Gdybym zadeklarować ją jako

void print_stream(const istream& is); 

następnie wdrożenie danej funkcji nie będzie kompilować, ponieważ nie można odczytać z const istream.

Nie mogę uczynić z funkcji szablonu i użyć "uniwersalnego odniesienia", ponieważ jego implementacja musi być osobno skompilowana.

mogę dostarczyć dwa przeciążeń:

void print_stream(istream& is); 
void print_stream(istream&& is); 

i mieć drugiego naboru w pierwszym, ale to wydaje się dużo niepotrzebnej boilerplate, i uważam, że to bardzo niefortunne, aby to zrobić za każdym razem kiedy napisz funkcję z taką semantyką jak ta.

Czy jest coś lepszego, co mogę zrobić?

+0

Mogłeś mieć pusty wydruk (Wrapper) i przenieść problem do konstruktora Wrapper, co ma sens tylko wtedy, gdy chcesz zrobić to samo dla kilku funkcji. –

+2

"za każdym razem, gdy piszę funkcję z taką semantyką"? Czy często piszemy funkcje, które muszą przyjmować niestanowiący stałej nieciągłości argument, i muszą być wywoływalne zarówno z parametrem tymczasowym, jak i lwartościowym? Jest to dość specyficzny przypadek, a nie taki, w którym bym się martwił "za każdym razem, gdy muszę napisać funkcję z taką semantyką". Jeśli parametr można skopiować, możesz po prostu przekazać go według wartości. Jeśli nie musiałeś wywoływać na nim funkcji niestanowiących stałych, mógłbyś przejść przez referencję do konstelacji – jalf

+2

Możesz napisać pomocnik rzucający "szablon T & stay (T && t) {return t; } 'i użyj' stay (fstream {"filename"}) '. –

Odpowiedz

15

Nie ma wiele z innego wyboru niż oferowanie dwóch przeciążeń lub sprawienie, że funkcja będzie szablonem, powiedziałabym.

Jeśli naprawdę, naprawdę potrzebują (brzydki) alternatywę, to chyba jedyna (szalony), co można zrobić, to mieć swoją funkcję zaakceptować const&, z warunkiem wstępnym mówiąc, że nie można przekazać obiekt typu const - kwalifikowany typ (nie chcesz tego mimo to wspierać). Funkcja mogłaby wtedy odrzucić odniesienie do pliku referencyjnego.

Ale ja osobiście napisać dwa przeciążeń i zdefiniować jeden, jeśli chodzi o innych, więc to zrobić duplikat deklaracji, ale nie definicję:

void foo(X& x) 
{ 
    // Here goes the stuff... 
} 

void foo(X&& x) { foo(x); } 
+1

to nie jest dobrze skalowane (np. 3 parametry -> 8 wymaganych funkcji). Czy istnieje jakiś sposób? Jestem w tej samej pozycji co OP. – Dave

+1

@Dave Czy coś takiego działa w twoim przypadku użycia? http://coliru.stacked-crooked.com/a/e93ea8c3b693b593 – Mysticial

+0

@Mysticial to całkiem miłe obejście, ale niestety 3 lata później zupełnie zapomniałem, czego potrzebowałem. Będę o tym pamiętał na wypadek, gdyby kiedykolwiek się pojawił! – Dave

4

Inną alternatywą jest raczej brzydki, aby Funkcja A szablon i jawnie utworzyć obie wersje:

template<typename T> 
void print(T&&) { /* ... */ } 

template void print<istream&>(istream&); 
template void print<istream&&>(istream&&); 

Można to skompilować osobno. Kod klienta wymaga tylko deklaracji szablonu.

Osobiście po prostu trzymam się tego, co sugeruje Andy Prowl.

+0

@ HighCommander4 [Działa dla mnie.] (Http://coliru.stacked-crooked.com/view?id=fa0857865f55a2615158e93e8ceb5a76-3725be9f9ce62f113fc473b4ae69c419) – jrok