Variadic模板和类型扣除问题

时间:2013-11-01 13:51:56

标签: c++ c++11 type-conversion variadic-templates

2012 ACCU C++ Pub quiz的问题15中,我对结果感到困惑。

#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 3foo 3foo 2foo 2foo 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 Express 2013上试过它,它提供了1243548,也没有任何警告。

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

2 个答案:

答案 0 :(得分:3)

它看起来像是声明的顺序。如果你转发声明foo的相关重载超过foo 2那么你会看到你期望的结果,即把它放在foo 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 :(得分:1)

foo 2无法致电foo 3,因为foo 3不在foo 2的范围内。