まだ考えてました。

#include <tuple>
#include <iostream>
#include <string>
#include <functional>
#include <stdexcept>

void printf(const char *s)
{
  while (*s)
  {
    if (*s == '%' && *(++s) != '%')
      throw std::runtime_error("invalid format string: missing arguments");
    std::cout << *s++;
  }
}
 
template<typename T, typename... Args>
void printf(const char* s, T value, Args... args)
{
  while (*s)
  {
    if (*s == '%' && *(++s) != '%')
    {
      std::cout << value;
      printf(*s ? ++s : s, args...);
      return;
    }
    std::cout << *s++;
  }
  throw std::runtime_error("extra arguments provided to printf");
}

template <class... Args>
void do_something(Args&&... args) {
  printf("%d%s%f", args...);
}

template <class... Args>
class functor {
  std::tuple<Args...> args_;
//  auto f_;
public:
  functor(Args&&... args)
    : args_(std::move(args)...)
  {
    auto f_ = std::bind(do_something<Args...>, args...);
    f_();
  } // 可変引数をメンバに保持しておいて

  void operator()() const
  {
    // 保持しておいた可変引数を別な関数に渡す
//    f_();
  }
};

int main() {
  functor<int, std::string, double> f(1, "hellow", 3.1);
  f();
  return 0;
}

std::bindの戻り値をメンバーに持つ方法がまだわかってないけど出来そうじゃないかな?