【问题标题】:Variadic template and type deduction issue可变参数模板和类型推导问题
【发布时间】:2013-11-01 13:51:56
【问题描述】:

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

【问题讨论】:

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


    【解决方案1】:

    它看起来像它的声明顺序。如果你在 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 方面似乎是错误的。

    【讨论】:

      【解决方案2】:

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

      【讨论】:

      • 根据标准,第 3.3 节,这正是“范围”。
      • 所以这是 Visual Studio 中的一个错误?
      • 是的,Visual Studio 编译器以不符合标准的方式实现模板。
      猜你喜欢
      • 1970-01-01
      • 2015-12-09
      • 1970-01-01
      • 2014-02-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-29
      相关资源
      最近更新 更多