va_list和省略号之间的重载分辨率因文字值而异.为什么



我有两个重载函数,其中唯一的区别是最后一个参数,一种是va_list,另一种是省略号。我注意到所选的重载是不同的,这取决于最后一个参数是整数文字0还是1。

我做了一个简化的工作示例,如下所示,我正在试验各种输入,并发现了一些其他奇怪的场景:

#include <iostream>
#include <cstdarg>
void Func(va_list /*arg*/)
{
std::cout << "void Fun(va_list);" << std::endl;
}
void Func(...)
{
std::cout << "void Fun(...);" << std::endl;
}
int main()
{
std::cout << "Func(0);"  << " >> ";
Func(0);    
std::cout << "Func(1);"  << " >> ";
Func(1);
std::cout << "Func(-1);" << " >> ";
Func(-1);
std::cout << "Func('0');"   << " >> ";
Func('0');    
std::cout << "Func('\0');" << " >> ";
Func('');

std::cout << "Func("");"      << " >> ";
Func("");    
std::cout << "Func("Dummy");" << " >> ";
Func("Dummy");
std::cout << "Func(nullptr);"   << " >> ";
Func(nullptr);

std::cout << "Func(0*1);"      << " >> ";
Func(0*1);
std::cout << "Func(0.0);"      << " >> ";
Func(0.0);

std::cout <<"int i = 0; Func(i);" << " >> ";
int i = 0;
Func(i);
std::cout <<"i = 1; Func(i);" << " >> ";
i = 1;
Func(i);

static const int j = 0, k = 1;
std::cout << "static const int j = 0; Func(j);" << " >> ";
Func(j);
std::cout << "static const int k = 1; Func(k);" << " >> ";
Func(k);    
}

使用C++14在cppshell中编译此代码会得到以下输出:

Func(0); >> void Func(va_list);
Func(1); >> void Func(...);
Func(-1); >> void Func(...);
Func('0'); >> void Func(...);
Func(''); >> void Func(va_list);
Func(""); >> void Func(...);
Func("Dummy"); >> void Func(...);
Func(nullptr); >> void Func(va_list);
Func(0*1); >> void Func(va_list);
Func(0.0); >> void Func(...);
int i = 0; Func(i); >> void Func(...);
i = 1; Func(i); >> void Func(...);
static const int j = 0; Func(j); >> void Func(...);
static const int k = 1; Func(k); >> void Func(...);

正如您所看到的,一些调用解析为va_list重载,而大多数调用解析为省略号重载。

我相信这种奇怪可能是因为va_list经常是char*。在Visual C++中,我发现它在vadefs.h中被定义为typedef char* va_list;。然而,我仍然不明白过载解决方案是如何在这里的大多数情况下发挥作用的。

  • 如何在va_list和省略号之间进行过载解析
  • 如何调用Func(0)来解决省略号重载问题

只有在没有到va_list的隐式转换序列的情况下,才会选择...重载。换句话说,省略号在过载解决过程中总是具有尽可能低的优先级。

由于va_list的定义是未指定的,因此您观察到的结果通常是不可移植的:Func(x)可以根据实现调用va_list重载或省略号重载。一个例外是,当x本身具有类型va_list(可能是const限定的(时,Func(x)将始终调用va_list重载。

事实上,如果系统上的va_listchar*,则可能发生空指针转换:换句话说,0可以隐式转换为类型为char*的空指针。任何其他整数值都不能隐式转换为指针值。

从C++14开始,即使是''也不应该隐式转换为指针值。空指针转换仅适用于值为零的整数文本。(尽管''具有整数类型,但它不是整数文本。(此特定版本的Visual Studio已过期。

至于你的问题";如何调用Func(0(来解析省略号重载&";,好吧,你不能改变过载解决的规则。您可以做的是将省略号重载更改为模板:

template <class T>
void Func(T x);

这保证了只有当参数为va_list(可能是const限定的(时才会调用va_list重载,并且在所有其他情况下,都会调用模板,因为它会给出完全匹配。然而,一个问题仍然存在:您可能有一些char*变量,您认为它不是va_list,并且用它作为参数调用Func可能会调用va_list重载,因为va_listchar*(在您的系统上(。没有办法阻止这种情况。也许你根本不应该超载Func。您可以使用两个单独的函数。

最新更新