c++ - Variadic 模板和类型推导问题

标签 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 3, foo 3, foo 2, foo 3, foo 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 中的编译器错误,还是规范中那些尴尬的未指定行为部分之一?

最佳答案

看起来是声明的顺序。如果你在 foo 2 之上转发声明 foo 的相关重载,那么你将看到你期望的结果,即将它放在 foo 2 之上:

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

标准的相关部分在 3.4.1.4 中:

A name used in global scope, outside of any function, class or user-declared namespace, shall be declared before its use in global scope.

并且在 14.6.4.1 从属名称解析中:

In resolving dependent names, names from the following sources are considered:

— Declarations that are visible at the point of definition of the template.

— Declarations from namespaces associated with the types of the function arguments both from the instantiation context (14.6.4.1) and from the definition context.

由于 args 是依赖类型,名称解析仅将可见的名称视为模板定义点。 foo 3 此时尚未声明,因此无法在重载决议中考虑。在此基础上,Visual Studio 允许使用 foo 3 似乎是错误的。

关于c++ - Variadic 模板和类型推导问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19728109/

相关文章:

c++ - 是否有可能有一个非递归的 at_c 实现?

c++ - 缓存单个重写计算 C++11

c++ - 编译器对我的类型转换方法做了什么

c++ - 函数指针的静态初始化

c++ - 拆分字符串

c++ - 在排序数组中查找旋转点

c++ - Char 到 int8_t 的转换给出了意想不到的结果?

c++ - 我可以使用 dlopen 使用 C 从 C++ 编译的共享库吗

c++ - 在循环中将值传递给数组

c - 解释将Float转换为整数时的输出?