1

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

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

4

2 回答 2

3

它看起来像它的声明顺序。如果您在 foo 2 之上转发声明 foo 的相关重载,那么您将看到您预期的结果,即把它放在 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.

于 2013-11-01T14:22:06.660 回答
1

foo 2无法调用foo 3,因为foo 3不在 的范围内foo 2

于 2013-11-01T14:01:30.713 回答