如何在 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 的可变模板版本转发给它。