Как на C++ узнать количество передаваемых аргументов?
Быстро и легко. Считаем с помощью вариативных шаблонов и оператора sizeoftemplate <typename ... Args> int ArgsCount(Args ... args) {
    return sizeof...(args);
}

std::cout << ArgsCount(1, "hello", 2.f); // Вывод: 3
Какие есть способы физически, на уровне машинного кода, передать переменное число параметров в подпрограмму?
1. Автобоксинг в массив. Если к тому же тип элементов может быть любым — тогда будет массив «лёгких» или обычных variant’ов, или массив объектов на «куче». В Delphi для этого используется лёгкий variant (см. SysUtils.Format), в Java и других «мусорных» языках — объекты на куче. В Си++ нет.
2. Автоматически развернуть такой вызов в кучу вызовов поменьше. См. вариативные шаблоны C++11 = variadic templates. В Си++ есть, штука тяжёлая, я с ней и сам знаком поверхностно и ничего толком рассказать не могу. Но вот кое-что набросал.#include <iostream>

constexpr int countArgs() { return 0; }

template <class Arg, class ... Args>
constexpr int countArgs(const Arg& x, const Args& ... args)
{
    return countArgs(args...) + 1;
}

int main()
{
    std::cout << countArgs() << std::endl;
    std::cout << countArgs(1, 2, 3) << std::endl;
    return 0;
}
Может быть, и первый путь удастся завернуть в массив через вариативные шаблоны, но я не в курсе. А вот обрабатывать аргументы по одному — за милую душу!
3. Использовать особые соглашения вызова и раскручивать стек, пока не попадётся какой-то маркер «больше параметров нет» (см. работу с формами cURL), или окольным путём узнать количество параметров (см. printf). Есть даже в Си (который не что иное, как «ассемблер высокого уровня»), штука очень системная и чреватая ошибками.
4. Возможна ещё и такая фишка: физически оно устроено как printf, но обёрнуто в «лёгкий» вариативный шаблон, который  защищает всё это добро от ошибок программиста.#include <iostream>
#include <cstdarg>

constexpr int countArgs() { return 0; }

template <class ... Args>
constexpr int countArgs(int x, Args ... args)
{
    return countArgs(args...) + 1;
}

void outArgsInner(int count, ...)
{
    va_list ap;
    va_start(ap, count);
    if (count > 0) {
        std::cout << va_arg(ap, int);
        for (int i = 2; i <= count; ++i) {
            std::cout << ' ' << va_arg(ap, int);
        }
    }
    va_end(ap);
    std::cout << std::endl;
}


template <class ... Args>
inline void outArgs(Args ... args)
{
    outArgsInner(countArgs(args...), args...);
}

int main()
{
    outArgs();      // пустой тоже работает
    outArgs(1, 2, 3);
    // outArgs("a", 2, 3);   тут ошибка! — и верно, мы принимаем только int’ы
    return 0;
}