如何在 C++ 中消除可变参数函数与另一个函数的歧义
How to disambiguate a variadic function with another in C++
假设我想让一个函数被两个版本重载,如下所示:
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("...");
为了解决这种情况下的歧义,我尝试了几种方法,但其中 none 行得通:
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 抱怨警告:
"second parameter of va_start
is not last named argument"
即使我忽略那个警告,它也不起作用。无论我尝试什么,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
是内置的而不是来自库。
有些人会说实际上在两个版本中,第二个版本取代了第一个版本。这意味着如果我删除第一个版本并将其内部放入第二个版本,那么一切都很好。但我有充分的理由保留第一个版本:我希望只使用一个字符串的调用直接进行,而不会不必要地调用 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
的可变模板版本转发给它。
假设我想让一个函数被两个版本重载,如下所示:
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("...");
为了解决这种情况下的歧义,我尝试了几种方法,但其中 none 行得通:
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 抱怨警告:
"second parameter of
va_start
is not last named argument"
即使我忽略那个警告,它也不起作用。无论我尝试什么,vargs
都无法捕获 value
参数:va_start(vargs, s)
或 va_start(vargs, value)
。在我看来,无论我们向 va_start
.
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
是内置的而不是来自库。
有些人会说实际上在两个版本中,第二个版本取代了第一个版本。这意味着如果我删除第一个版本并将其内部放入第二个版本,那么一切都很好。但我有充分的理由保留第一个版本:我希望只使用一个字符串的调用直接进行,而不会不必要地调用 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
的可变模板版本转发给它。