我知道va_list
通常是您应该避免的,因为它不是很安全,但是可以通过以下功能传递参数:
void foo(...);
之类的函数 template<typename... Args>
void bar(Args... arguments);
?
编辑:最初,我想尝试使用它来调用具有可变数量参数/类型的虚拟函数,但这不是使这个问题无关紧要的方法。最终我最终做了这样的事情:
struct ArgsPackBase
{
virtual ~ArgsPackBase() {}
};
template<typename... Args>
struct ArgsPack : public ArgsPackBase
{
public:
ArgsPack(Args... args_)
: argsTuple(args_...)
{}
void call(std::function<void(Args...)> function)
{
callExpansion(function, std::index_sequence_for<Args...>{});
}
private:
template<std::size_t... I>
void callExpansion(std::function<void(Args...)> function, std::index_sequence<I...>)
{
function(std::get<I>(argsTuple)...);
}
std::tuple<Args...> argsTuple;
};
no,variadic函数参数是一个运行时功能,并且您将您传递给variadic模板的参数数量(尽管可变)必须在编译时知道。
如RFC1925所观察到的,"有足够的推力,猪会飞得很好。但是,这不一定是一个好主意。"
正如Piotr olszewski指向的那样,旧的C风格variadic函数参数是一个旨在在运行时工作的功能。新的variadic模板C - 汇编时间的样式工作。
所以...只是为了好玩...我想如果您知道,可以编译时间,foo()
的参数类型。
示例,如果 foo()
是一个variadic模板函数,则在以下示例中像 foo()
一样……该编译并与clang 一起使用,但使用g 给出汇编错误...而我不知道谁是对的(当我时有时间,我会为此打开一个问题)...
#include <cstdarg>
#include <iostream>
#include <stdexcept>
template <typename ... Args>
void bar (Args const & ... args)
{
using unused = int[];
(void)unused { (std::cout << args << ", ", 0)... };
std::cout << std::endl;
}
template <typename ... Ts>
void foo (int num, ...)
{
if ( num != sizeof...(Ts) )
throw std::runtime_error("!");
va_list args;
va_start(args, num);
bar( va_arg(args, Ts)... );
va_end(args);
}
int main ()
{
foo<int, long, long long>(3, 1, 2L, 3LL); // print 1, 2, 3,
}
观察到您需要在foo()
中传递一个补给信息:variadic参数的数量:va_start
语法要求您传递具有sizeof...(Ts)
值相同值的变量(num
)。
但是,我重复一遍,只是为了好玩。
为什么出于善意,当我们可以直接编写bar()
之类的函数时,我们应该写下像foo()
这样的函数?
对于C 模板,编译器必须在编译时产生每个实例。因此,对于每个参数组合(int,double,float)
,相应的实例应显示在对象文件中。
您的foo
不可能知道每个参数组合,因为有无限的金额 - 因此,除非您以某种方式限制参数空间,否则您的问题的答案是"否"。
但是,使用某些模板魔术是可能的,但实际上并不有用。我以一个特定的示例作为概念证明,但是请不要在真实代码。
中使用它。说
void foo(const char* s, ...);
期望像"ffis"
这样的格式字符串,其中每个字符都指定参数类型(在这种情况下为double,double,double,整数,字符串)。我们还具有一个variadic模板bar
函数,该函数打印其参数:
template <typename Arg, typename... Args>
void doPrint(std::ostream& out, Arg&& arg, Args&&... args)
{
out << std::forward<Arg>(arg);
using expander = int[];
(void)expander {
0, (void(out << ", " << std::forward<Args>(args)), 0)...
};
out << 'n';
}
void bar() {
std::cout << "no argumentsn";
}
template<typename... Args>
void bar(Args... arguments) {
doPrint(std::cout, arguments...);
}
对于foo
工作,我们将在编译时间生产每个可能的参数组合,直至长度N
(SO,3^n实例):
//struct required to specialize on N=0 case
template<int N>
struct CallFoo {
template<typename... Args>
static void foo1(const char* fmt, va_list args, Args... arguments) {
if (*fmt) {
using CallFooNext = CallFoo<N - 1>;
switch (*fmt) {
case 'f':
{
double t = va_arg(args, double);
CallFooNext::foo1(fmt + 1, args, arguments..., t);
}break;
case 'i':
{
int t = va_arg(args, int);
CallFooNext::foo1(fmt + 1, args, arguments..., t);
}break;
case 's':
{
const char* t = va_arg(args, const char*);
CallFooNext::foo1(fmt + 1, args, arguments..., t);
}break;
}
} else {
bar(arguments...);
}
}
};
template<>
struct CallFoo<0> {
template<typename... Args>
static void foo1(const char* fmt, va_list args, Args... arguments) {
bar(arguments...);
}
};
void foo(const char* fmt, ...) {
va_list args;
va_start(args, fmt);
//Here we set N = 6
CallFoo<6>::foo1<>(fmt, args);
va_end(args);
}
主要功能,用于完整性:
int main() {
foo("ffis", 2.3, 3.4, 1, "hello!");
}
由此产生的代码在我的计算机上使用gcc
编译约10秒,但生成正确的字符串2.3, 3.4, 1, hello!