我知道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;
};
答案 0 :(得分:2)
不,可变参数函数参数是运行时特性,并且必须在编译时知道传递给可变参数模板的参数数量(尽管是变量)。
答案 1 :(得分:1)
如在RFC1925中所观察到的那样,#34;如果有足够的推力,猪就会飞得很好。但是,这不一定是个好主意。&#34;
正如Piotr Olszewski指出的那样,旧的C风格的可变参数函数参数是一个旨在在运行时工作的特性;新的可变参数模板C ++ - 在编译时工作。
所以......只是为了好玩......我想如果你知道,编译时间,foo()
的论证类型就可以。
例如,如果foo()
是一个可变参数模板函数,例如以下示例中的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()
中传递reduntant信息:可变参数的数量:va_start
语法要求您传递具有相同值的变量(num
) sizeof...(Ts)
。
但是,我重复一遍,只是为了好玩。
为什么我们应该直接编写像foo()
这样的函数来编写像bar()
这样的函数?
答案 2 :(得分:0)
对于C ++模板,编译器必须在编译时生成每个实例。因此,对于每个参数组合(int,double,float)
,相应的实例应出现在目标文件中。
您的foo
无法知道每个参数组合,因为有无限量 - 所以除非您以某种方式限制参数空间,否则问题的答案是&#34; no&#34;。< / p>
然而,有了一些模板魔术,可能,但实际上并不实用。我将一个具体示例显示为概念证明,但请不要在实际代码中使用它。
让我们说
void foo(const char* s, ...);
期望格式字符串如"ffis"
,其中每个字符指定一个参数类型(在这种情况下为double,double,integer,string)。我们还有一个可变参数模板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 arguments\n";
}
template<typename... Args>
void bar(Args... arguments) {
doPrint(std::cout, arguments...);
}
要使foo
起作用,我们将在编译时生成每个可能的参数组合,长度为N
(因此,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!