为未定义函数选择正确重载的编译器差异

Compiler differences on choosing the right overload for undefined function

让我们有以下代码:

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

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

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

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

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

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

它在 MSVC2015 和 ideone's GCC

上的行为不同

args...等于48, '4', '5'时,调用foo。编译器因调用 foo 而异。 MSVC 考虑了在实际调用中定义的重载(之前没有声明),而 GCC 只适用于已经声明的重载。如果我取消注释声明,MSVC 和 GCC 都会打印 1 2 4 3 5 48。如果声明被注释掉,MSVC 打印相同,但 GCC 打印 1 2 3 5 52 48 (52 is ascii for 4)

哪个编译器符合标准?

我有 n4296 草稿,但我在那里找不到答案,该文件对我来说有点不可读...

MSVC 未能按照标准规定为模板实施 two phase name lookup。此编译器错误地将所有名称解析推迟到模板 instantiation.

其他编译器在模板定义 处正确解析非依赖名称。这就是您观察到的差异的原因。在定义点,第二个 foo 模板是不可见的,因此符合规范的编译器无法使用它。但在实例化时,它是可见的,MSVC 很高兴地找到了它。