Musisz wykorzystać tutaj pewien trick.
Najpierw kod, potem objaśnienie:
#include <iostream>
#include <utility> // dla std::forward
template <typename ArgT, typename... ArgsT>
void Show(ArgT &&firstArg, ArgsT&&... rest)
{
std::cout << std::forward<ArgT>(firstArg);
using expander = int[];
(void)expander {
(void(std::cout << std::forward<ArgsT>(rest)), 0)...
};
}
int main()
{
Show("A string, a number = ", 32, ", a floating point: ", 123.400f, "\n");
return 0;
}
Funkcja Show z góry ma zadanie wyświetlać cokolwiek, więc liczba jej parametrów będzie >= 1, dlatego podajemy pierwszy argument "firstArg" oddzielnie a resztę jako listę argumentów, którą potem będziemy "rozwijali". To co może Cię zdziwić to tzw. "forwarding references" czyli "&&" - nie jest to w żadnym wypadku tutaj "&&", który używamy w ifach. Moje krótkie wyjaśnienie to: używamy rvalue reference (bo taka jest druga nazwa tego) by zapewnić możliwość zachowania typu wszystkich tych argumentów - nie kopiować ich podczas wykonywania funkcji itd. Zachęcam do przeczytania tego artykułu, świetnie to wprowadza do forwarding refs:
Forwarding references
Dalej mamy przekazanie do strumienia cout pierwszego argumentu - żeby poprawnie przekazać argument typu T&& używamy std::forward<T>. Ta linijka spowoduje, że pierwszy argument zostanie wyświetlony. Teraz dzieje się "magia":
- dla ułatwienia zapisujemy sobie typ tablicy intów ( int[] ) jako "expander"
- konstruujemy nienazwaną tablicę tego typu (tak samo jakbyś napisał
int[] {0, 1, 2, 4, 5, 6};
- (void) przed tym skonstruowaniem oznajmia kompilatorowi, że nie będziemy mieć pożytku z tej tablicy, zostanie ona zapomniana zaraz po jej skonstruowaniu
- Teraz zerujemy każdy element tablicy a będzie ich liczba_argumentow-1 (bo pierwszy argument już został dodany)
Ostatnie zdanie może wydawać się trochę mylące, bo co innego widzimy w kodzie.
Działa to w ten sposób:
Zapisując wyrażenie (a, b) powodujemy, że zostanie wykonane "a" ale cała wartość tego nawiasu przyjmie wartość "b". Tutaj przykład:
int x = 10, y = 20, z = 30;
x = (y, z); // ostatecznie x przyjmie wartosc "z", bo jest po prawej stronie
Znając ten fakt, wystarczy tylko po prawej stronie umieścić zero (lub jakakolwiek inną wartość, ale zero tradycyjnie jest ok) a po lewej zdołać wyświetlić następny argument. Robimy to w ten sposób:
std::cout << std::forward<ArgsT>(rest)
Wartość tego wyrażenia to std::ostream&, gdyż operator << zwraca tą wartość. W takim razie, żeby zakomunikować, że pomijamy zwróconą wartość zamykamy to w voidzie:
void(std::cout << std::forward<ArgsT>(rest))
Teraz, po napisaniu tego polecenia według schematu (a, b) otrzymujemy:
(void(std::cout << std::forward<ArgsT>(rest)), 0)
Na koniec trzeba jeszcze "rozwinąć" listę argumentów, tak jak rozwija się zawinięte kartki papieru, przez operator "..." na końcu nawiasu. W ten sposób do strumienia std::cout przekażemy wszystkie argumenty zawarte w "rest".
(void(std::cout << std::forward<ArgsT>(rest)), 0)...
Mam nadzieję, że jasno to opisałem, jak coś - proszę napisać komentarz.