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

发布于 2025-01-06 00:51:02 字数 620 浏览 3 评论 0原文

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

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

class S
{
    struct P {};
};

template<typename T>
S::P Bat(T);

在 MSVC10 中干净地编译:

E:\clangbuild\bin\Release>cl /c /nologo test.cpp
test.cpp

在 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.

The following is a piece of test code, and I'm comparing the result of compiling this with MSVC and Clang respectively. The output of each compiler is shown below. MSVC pretends that the unused template declaration doesn't even exist. Clang produces an error. The question is, which compiler is most standard conformant here?

I have seen legacy production code that relies on the MSVC behavior, and I'm unsure whether or not it can continue to be relied on.

class S
{
    struct P {};
};

template<typename T>
S::P Bat(T);

Compiles cleanly in MSVC10:

E:\clangbuild\bin\Release>cl /c /nologo test.cpp
test.cpp

Produces an error in 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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

桃扇骨 2025-01-13 00:51:02

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

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

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

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

可怕的两相名称查找

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

14.6.8:

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

14.6.9:

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

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

访问规则(第 11 条)仅在名称查找和
函数重载解析(如果适用)已成功。仅有的
在名称查找、函数重载解析(如果适用)之后
访问检查已成功是由
name 的声明在表达式处理中进一步使用(第 5 条)。

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

This fails because of the two-phase name lookup in C++.

In phase one, when the template is initially parsed, long before it is instantiated, the compiler parses the template and looks up any non-dependent names. S::P is a non-dependent name, so the compiler tries to look it up, but fails because it is private.

In phase 2, when the template is instantiated, the compiler will lookup any dependent names, which can vary from template to template.

Clang is fairly strictly conforming to the two-phase name lookup. However, MSVC has a template parsing model that delays nearly every lookup to instantiation time, which is part of phase 2. This delay is why your example would compile with MSVC(which is non-conforming) and not in clang. Here is a link with more information:

The Dreaded Two-Phase Name Lookup

Also, here are the sections from the C++ standard where it describes the two-phase lookup.

14.6.8:

When looking for the declaration of a name used in a template
definition, the usual lookup rules (3.4.1, 3.4.2) are used for
non-dependent names. The lookup of names dependent on the template
parameters is postponed until the actual template argument is known.

14.6.9:

If a name does not depend on a template-parameter (as defined in
14.6.2), a declaration (or set of declarations) for that name shall be in scope at the point where the name appears in the template
definition; the name is bound to the declaration (or declarations)
found at that point and this binding is not affected by declarations
that are visible at the point of instantiation.

Then the part of 3.4 Name lookup applicable to you:

The access rules (clause 11) are considered only once name lookup and
function overload resolution (if applicable) have succeeded. Only
after name lookup, function overload resolution (if applicable) and
access checking have succeeded are the attributes introduced by the
name’s declaration used further in expression processing (clause 5).

Its clear from reading these parts that your program is ill-formed. The only thing the standard states that should be postponed until instantiation is the lookup of a dependent name. Non-dependent names go through the usual name lookup, which includes access rules.

停顿的约定 2025-01-13 00:51:02

编译器实际上只需要检查未实例化模板声明的任何格式错误的语法。任何额外的语义评估仅需要在实例化模板函数时完成。

由于 S::P 确实是一个可以从函数返回的有效类型,因此它们同样一致。

The compiler is only really required to check for any malformed syntax of uninstantiated template declarations. Any additional semantic evaluation only needs to be done when the template function is instantiated.

Since S::P is indeed a type which is valid to be returned from a function, they are both equally conformant.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文