Jak napisać funkcję, która akceptuje zmienną liczbę argumentów? Czy to możliwe, jak?
c++
variadic-functions
nunos
źródło
źródło
Odpowiedzi:
Prawdopodobnie nie powinieneś i prawdopodobnie możesz robić to, co chcesz robić w bezpieczniejszy i prostszy sposób. Technicznie, aby użyć zmiennej liczby argumentów w C, dołączamy stdarg.h. Od tego otrzymasz
va_list
typ, a także trzy działające na nim funkcje o nazwieva_start()
,va_arg()
iva_end()
.Jeśli mnie zapytasz, to jest bałagan. Wygląda źle, jest niebezpieczny i pełen technicznych szczegółów, które nie mają nic wspólnego z tym, co koncepcyjnie próbujesz osiągnąć. Zamiast tego rozważ użycie przeciążenia lub dziedziczenia / polimorfizmu, wzorca konstruktora (jak w
operator<<()
strumieniach) lub domyślnych argumentów itp. Wszystko to jest bezpieczniejsze: kompilator dowie się więcej o tym, co próbujesz zrobić, więc jest więcej okazji, aby zatrzymać zanim zdejmiesz nogę.źródło
...
składnią?printf()
, na przykład, funkcja analizuje argument ciągu dla specjalnych tokenów, aby dowiedzieć się, ile dodatkowych argumentów powinien się spodziewać na liście zmiennych.<cstdarg>
w C ++ zamiast<stdarg.h>
W C ++ 11 masz dwie nowe opcje, ponieważ strona referencyjna funkcji Variadic w sekcji Alternatywy mówi:
Poniżej znajduje się przykład pokazujący obie alternatywy ( zobacz na żywo ):
Jeśli używasz
gcc
lubclang
możemy użyć magicznej zmiennej PRETTY_FUNCTION, aby wyświetlić podpis typu funkcji, który może być pomocny w zrozumieniu, co się dzieje. Na przykład za pomocą:w poniższym przykładzie wyniki dla funkcji variadic ( zobacz na żywo ):
W Visual Studio możesz użyć FUNCSIG .
Zaktualizuj wersję C ++ 11
Przed wersją C ++ 11 alternatywą dla std :: initializer_list byłby std :: vector lub jeden z innych standardowych kontenerów :
a alternatywą dla szablonów variadic byłyby funkcje variadic, chociaż nie są one bezpieczne pod względem typu i generalnie podatne na błędy i mogą być niebezpieczne w użyciu, ale jedyną potencjalną alternatywą byłoby użycie domyślnych argumentów , chociaż ma to ograniczone zastosowanie. Poniższy przykład to zmodyfikowana wersja przykładowego kodu w powiązanym odnośniku:
Korzystanie z funkcji variadic wiąże się również z ograniczeniami w argumentach, które można przekazać, co jest szczegółowo opisane w projekcie standardu C ++ w sekcji
5.2.2
Wywołanie funkcji w paragrafie 7 :źródło
typename
vsclass
użytkowania powyżej zamierzone? Jeśli tak, proszę wyjaśnić.initializer_list
rekurencję?Rozwiązanie C ++ 17: pełne bezpieczeństwo typu + ładna składnia wywoływania
Od czasu wprowadzenia szablonów variadic w C ++ 11 i wyrażeń fold w C ++ 17 możliwe jest zdefiniowanie funkcji szablonu, która w miejscu wywołującym jest wywoływana tak, jakby była funkcją varidic, ale z zaletami :
Oto przykład mieszanych typów argumentów
I kolejne z wymuszonym dopasowaniem typu dla wszystkich argumentów:
Więcej informacji:
źródło
template<class Head, class... Tail, class = std::enable_if_t<are_same<Head, Tail...>::value, void>>
Head
iTail...
są takie same ”, gdzie „ są takie same ” oznaczastd::conjunction<std::is_same<Head, Tail>...>
. Przeczytaj ostatnią definicję, ponieważ „Head
jest taki sam jak wszystkieTail...
”.w c ++ 11 możesz wykonać:
inicjator listy FTW!
źródło
W C ++ 11 istnieje sposób tworzenia szablonów zmiennych argumentów, które prowadzą do naprawdę eleganckiego i bezpiecznego typu funkcji do wykonywania zmiennych argumentów. Sam Bjarne podaje ładny przykład printf wykorzystujący szablony zmiennych argumentów w C ++ 11FAQ .
Osobiście uważam to za tak eleganckie, że nawet nie zawracałbym sobie głowy funkcją zmiennej argumentów w C ++, dopóki ten kompilator nie będzie obsługiwał szablonów zmiennych zmiennych C ++ 11.
źródło
,
operatora z wyrażeniami fold). W przeciwnym razie nie sądzę.Funkcje variadic w stylu C są obsługiwane w C ++.
Jednak większość bibliotek C ++ używa alternatywnego idiomu, np. Podczas gdy
'c' printf
funkcja przyjmuje zmienne argumenty,c++ cout
obiekt używa<<
przeciążenia, które dotyczy bezpieczeństwa typu i narzędzi ADT (być może kosztem prostoty implementacji).źródło
std::initializer_lists
... A to już wprowadza ogromną złożoność prostego zadania.Oprócz varargs lub przeciążenia, możesz rozważyć agregację argumentów w std :: vector lub innych kontenerach (na przykład std :: map). Coś takiego:
W ten sposób zyskałbyś bezpieczeństwo typu i logiczne znaczenie tych różnych argumentów byłoby oczywiste.
Z pewnością takie podejście może mieć problemy z wydajnością, ale nie powinieneś się tym martwić, chyba że masz pewność, że nie możesz zapłacić ceny. Jest to swego rodzaju „Pythońskie” podejście do c ++ ...
źródło
Jedynym sposobem jest użycie argumentów zmiennych w stylu C, jak opisano tutaj . Pamiętaj, że nie jest to zalecana praktyka, ponieważ nie jest bezpieczna dla maszyn i podatna na błędy.
źródło
Nie ma standardowego sposobu na zrobienie tego bez uciekania się do varargs (
...
) w stylu C.Istnieją oczywiście domyślne argumenty, które wyglądają jak zmienna liczba argumentów w zależności od kontekstu:
Wszystkie cztery wywołania funkcji wywołują
myfunc
zmienną liczbę argumentów. Jeśli nie podano żadnych, używane są domyślne argumenty. Należy jednak pamiętać, że można pominąć tylko końcowe argumenty. Nie można na przykład pominąći
i podać tylkoj
.źródło
Możliwe, że chcesz przeciążenia lub parametrów domyślnych - zdefiniuj tę samą funkcję z parametrami domyślnymi:
Umożliwi to wywołanie metody za pomocą jednego z czterech różnych wywołań:
... lub możesz szukać konwencji wywoływania v_args z C.
źródło
Jeśli znasz zakres liczby argumentów, które zostaną podane, zawsze możesz użyć przeciążenia funkcji, np
i tak dalej...
źródło
Używając szablonów variadic, przykład do reprodukcji,
console.log
jak widać w JavaScript:Nazwa pliku np .
js_console.h
:źródło
Jak powiedzieli inni, varargs w stylu C. Ale możesz również zrobić coś podobnego z domyślnymi argumentami.
źródło
Jest to możliwe teraz ... przy użyciu boost any i szablonów W tym przypadku typ argumentów można mieszać
źródło
Połącz rozwiązania C i C ++, aby uzyskać najprostszą semantycznie, wydajną i najbardziej dynamiczną opcję. Jeśli spieprzysz, spróbuj czegoś innego.
Użytkownik pisze:
Semantycznie wygląda to i działa dokładnie jak funkcja n-argumentowa. Pod maską możesz go rozpakować w taki czy inny sposób.
źródło
Możemy również użyć listy inicjalizującej, jeśli wszystkie argumenty są const i tego samego typu
źródło
Wersja zwykła
źródło