2013-11-01 173 views
1

2012 ACCU C++ Pub quiz的问题15中,我被结果难倒了。Variadic模板和类型扣除问题

#include <iostream> 

template<typename T> void P(T x) { std::cout << x; } 

void foo(char a) { // foo 1 
    P(3); 
    P(a); 
} 

template <typename... A> // foo 2 
void foo(int a, A... args) { 
    foo(args...); 
    P(a); 
} 

template <typename... A> 
void foo(char a, A... args) { // foo 3 
    P(a); 
    foo(args...); 
} 

int main() 
{ 
    foo('1','2',48,'4','5'); 
} 

我的理由是,它会调用foo 3foo 3foo 2foo 3foo 1,从而给出1243548输出。实际输出为12355248,并在我的调试器中确认如下:foo 3,foo 3,foo 2,foo 2, foo 1。我不知道为什么第四个foo电话打到foo 2而不是foo 3

仅供参考,我使用gcc 4.8.1 g++ -g -Wall -std=c++11 -Weffc++ -Wextra -O0 /tmp/foo.cpp -o /tmp/foo进行编译,根本没有任何警告。


编辑:我刚在Visual Studio速成2013年,它给1243548,也没有警告。

这是GCC/VS中的编译器错误,还是规范中那些尴尬的未指定行为部分之一?

回答

3

它看起来像它的声明顺序。如果向前声明富上述FOO 2的相关超载然后你会看到你所期望的结果,即把这个上面富2:

template <typename... A> 
void foo(char a, A... args); 

标准的相关部分是3.4.1.4:

在全局范围内使用的名称,在任何函数,类或 用户声明的名称空间之外,应在其全局使用之前声明。

而在14.6.4.1依赖名称解析:

在解决相关的名称,从下列来源名称是 认为:

- 这是在定义 点可见声明的模板。

- 与来自实例化上下文 (14.6.4.1)和定义上下文的函数参数的 类型关联的命名空间的声明。

由于args是一个依赖类型,名称解析仅考虑可见的名称作为模板定义的要点。 foo 3尚未被此点声明,因此不能在重载分辨率中考虑。在此基础上,Visual Studio在允许使用foo 3时出现错误。

1

foo 2不能称之为foo 3因为foo 3是不是在foo 2范围。

+0

根据3.3节的标准,这正是“范围”。 –

+0

所以这是Visual Studio中的错误? –

+0

是的,Visual Studio编译器以不符合标准的方式实现模板。 –