假设我想让一个函数重载两个版本,如下所示:
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)
.在我看来,无论我们向va_start
提供什么作为第二个参数,GCC 总是只将未命名的参数放入vargs
。
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 个版本取代了第一个版本。这意味着如果我删除第一个版本并将其内部放入第二个版本,那么一切都很好。但是我有一个很好的理由保留第一个版本:我希望只有字符串的调用直接进行,而无需不必要地调用vsnprintf
。所以请不要这样建议我。
我还考虑过将第一个版本合并到第二个版本中,然后在内部计算给定参数的数量以知道如何进行。但它似乎没有一个标准的方法可以做到这一点。使用可变参数模板可以确定参数的数量,但不能使用可变参数函数。如果我切换到可变参数模板,我就不能再在 GCC 中使用va_start
了。
希望有人能帮忙!!
我还没有测试过这个,但以下方法不会起作用吗?
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
转发到它。