假设我想要一个像这样的两个版本重载的函数:
a)void query(const string& s);
,它向服务器发出SQL查询。
b)void query(const string& s,...);
,它构建由格式字符串和要替换的参数给出的查询字符串。在内部,这个版本看起来像(我隐藏细节,不会使问题过于复杂):
va_list vargs;
va_start(vargs, s);
// ... call vsnprintf to build the query string
// ... call the first version with the query string
va_end(vargs);
请注意,我也希望这在MSVC和GCC中都有效。当然,通过如上所述的写作,由于含糊不清,我不能进行以下调用:
query("...");
为解决这种情况的歧义,我尝试了几种方法,但没有一种方法可行:
1)将它们改写为:
void query(const string& s) {
// ...
}
template<typename Value>
void query(const string& s, Value value,...) {
va_list vargs;
va_start(vargs, s);
// ...
}
这在MSVC中编译并正常工作,但GCC抱怨警告:
“
va_start
的第二个参数不是最后命名的参数”
即使我忽略了这个警告,它也不起作用。不知怎的,vargs
无法捕获value
参数,无论我尝试了什么:va_start(vargs, s)
或va_start(vargs, value)
。在我看来,无论我们提供什么作为vargs
的第二个参数,GCC始终只将未命名的参数带入va_start
。
2)将其重写为
void query(const string& s) {
// ...
}
template<typename... Values>
enable_if<(sizeof...(Values) > 0), void>::type
query(const string& s, Values value...) {
va_list vargs;
va_start(vargs, s);
// ...
}
同样,这将编译并与MSVC一起使用。但GCC抱怨错误,第二个版本是可变参数模板而不是可变参数函数,并且不允许在那里使用va_start
。似乎GCC中的va_start
是内置的,而不是来自库。
有些人可以说实际上在2个版本中,第2版取代了第1版。这意味着如果我删除第一个版本并将其内部放入第二个版本,那么一切都没问题。但是我有充分的理由保留第一个版本:我希望只有一个字符串的调用直接进行,而不必不必要地调用vsnprintf
。所以请不要这样建议我。
我还考虑过将第一个版本合并到第二个版本,然后在内部计算给定参数的数量以了解如何去做。但它似乎没有标准的方法来做到这一点。使用可变参数模板但不能使用可变参数函数来确定参数的数量。如果我切换到可变参数模板,我不能再在GCC中使用va_start
。
希望有人能帮忙!!
答案 0 :(得分:1)
我还没有测试过这个,但是不会做以下工作吗?
void query_varargs(const string &s, ...) {
va_list vargs;
va_start(vargs, s);
// ...
}
template<typename... Values>
enable_if<(sizeof...(Values) > 0), void>::type
query(const string& s, Values value...) {
query_varargs(s, ...value);
}
即。将功能移动到不同的功能(query_varargs
),然后将query
的可变参数模板版本转发给它。