Clang vs MSVC:模板函数原型的处理

bre*_*anw 11 c++ templates clang visual-c++

以下是一段测试代码,我将分别编译它的结果与MSVC和Clang进行比较.每个编译器的输出如下所示.MSVC假装未使用的模板声明甚至不存在.Clang产生错误.问题是,哪个编译器最符合标准?

我已经看到依赖于MSVC行为的遗留生产代码,我不确定它是否可以继续依赖.

class S
{
    struct P {};
};

template<typename T>
S::P Bat(T);
Run Code Online (Sandbox Code Playgroud)

在MSVC10中干净地编译:

E:\clangbuild\bin\Release>cl /c /nologo test.cpp
test.cpp
Run Code Online (Sandbox Code Playgroud)

在Clang中产生错误:

E:\clangbuild\bin\Release>clang++ test.cpp
test.cpp:9:4: error: 'P' is a private member of 'S'
S::P Bat(T);
   ^
test.cpp:5:9: note: implicitly declared private here
struct P {};
        ^
1 error generated.
Run Code Online (Sandbox Code Playgroud)

Pau*_* II 4

由于 C++ 中的两阶段名称查找,此操作失败。

\n\n

在第一阶段,当模板最初被解析时,早在实例化之前,编译器就会解析模板并查找任何非依赖名称。S::P是一个非依赖名称,因此编译器尝试查找它,但由于它是私有的而失败。

\n\n

在第 2 阶段,实例化模板时,编译器将查找任何依赖名称,这些名称可能因模板而异。

\n\n

Clang 相当严格地遵循两阶段名称查找。然而,MSVC 有一个模板解析模型,它几乎延迟了每次查找到实例化时间,这是第 2 阶段的一部分。这种延迟就是为什么您的示例将使用 MSVC(这是不合格的)而不是 clang 进行编译。这是包含更多信息的链接:

\n\n

可怕的两阶段名称查找

\n\n

另外,以下是 C++ 标准中描述两阶段查找的部分。

\n\n

14.6.8:

\n\n
\n

当查找模板定义中使用的名称的声明时,通常的查找规则(3.4.1、3.4.2)用于非依赖名称。依赖于模板参数的名称查找将被推迟,直到知道实际的模板参数为止。

\n
\n\n

14.6.9:

\n\n
\n

如果名称不依赖于模板参数(如 14.6.2 中定义),则该名称的声明(或声明集)应位于该名称出现在模板定义中的范围内; 该名称绑定到此时发现的声明(或多个声明),并且此绑定不受实例化时可见的声明的影响。

\n
\n\n

那么 3.4 名称查找部分适用于您:

\n\n
\n

仅当名称查找和函数重载解析(如果适用)成功时才会考虑访问规则(第 11 条)。只有在名称查找、函数重载解析(如果适用)和访问检查成功之后,名称\xe2\x80\x99s 声明引入的属性才会在表达式处理中进一步使用(第 5 条)。

\n
\n\n

通过阅读这些部分可以清楚地看出您的程序格式不正确。该标准规定唯一应该推迟到实例化的事情是依赖名称的查找。非依赖名称会经过通常的名称查找,其中包括访问规则。

\n