2

作为我之前提出的一个问题的推论,我很好奇为什么模板中的名称类型(该名称的“类别”)是在 2 阶段查找的第一阶段设置的,而类别本身可以还取决于模板参数。这种行为的真正收获是什么?

一点澄清 - 我想我对 2 阶段查找的工作原理有相当了解我试图理解的是为什么在阶段 1 中明确确定令牌的类别,这与确定依赖类型时(在阶段 2)不同。我的论点是简化困难的语法,使代码更易于编写和阅读,这是一个非常实际的收获,所以我很好奇将类别评估限制在阶段 1 的令人信服的理由是什么。仅仅是为了在模板实例化之前获得更好的模板验证/错误消息,还是稍微提高速度?还是模板的某些基本属性使第 2 阶段类别评估不可行?

4

2 回答 2

2

问题可能有两个方面:为什么我们首先需要两阶段查找,并且鉴于我们有两阶段查找,为什么在第一阶段对令牌的解释是固定的。第一个是更难回答的问题,因为它是语言中的设计决策,因此它有其优点和缺点,并且取决于你的立场,或者其他人会更有分量。

您感兴趣的第二部分实际上要简单得多。为什么,在具有两阶段查找的 C++ 语言中,令牌含义在第一阶段是固定的,不能在第二阶段被解释。原因是 C++ 有上下文文法,而记号的解释高度依赖于上下文。如果不在第一阶段确定标记的含义,您甚至不会知道首先需要查找哪些名称。

考虑一个稍微修改过的原始代码版本,其中文字 5 被一个常量表达式替换,并假设您不需要提供最后一次咬您的templateortypename关键字:

const int b = 5;
template<typename T>
struct Derived : public Base<T> {
    void Foo() { 
       Base<T>::Bar<false>(b);   // [1]
       std::cout << b;           // [2]
    }
};

[1] 的可能含义是什么(忽略在 C++ 中这是通过添加typenameand来确定的事实template)?

  1. Bar是一个静态模板函数,它接受一个bool作为模板参数和一个整数作为参数。b是指常数 5 的非依赖名称。*

  2. Bar是一个嵌套模板类型,它采用单个bool作为模板参数。b是在函数内部定义Derived<T>::Foo但未使用的该类型的实例。

  3. Bar是一个类型的静态成员变量,X对其进行比较,该比较operator<采用 abool并产生一个U可以与operator>整数进行比较的类型的对象。

现在的问题是我们如何在模板参数被替换之前(即在第一阶段)继续解析名称。如果我们在情况 1. 或 3. 中,则b需要查找并且可以在表达式中替换结果。在第一种情况下产生您的原始代码:Base<T>::template Bar<false>(5),在后一种情况下产生operator>( operator<( Base<T>::Bar,false ), 5 ). 在第三种情况(2.)中,第一阶段之后的代码将与原始代码完全相同:(Base<T>::Bar<false> b;删除额外的())。

第二行 [2] 的含义取决于我们如何解释第一行 [1]。在 2. 情况下,它表示对 的调用operator<<( std::cout, Base<T>::Bar<false> & ),而在其他两种情况下,它表示operator<<( std::cout, 5 )。同样,其含义超出了第二个参数的类型,如 2. case name binsideDerived<T>::Foo是依赖的,因此它无法在第一阶段解决,而是推迟到第二阶段(它也会通过添加参数依赖查找的命名空间Base和实例化类型)。T

如示例所示,标记的解释会影响名称的含义,进而影响其余代码的含义,哪些名称是依赖的,因此在第一个过程中还需要查找哪些内容阶段。同时,编译器确实在第一遍期间执行检查,如果在第二遍期间可以重新解释标记,那么在第一遍期间的检查和查找结果将变得无用(想象在第一遍期间) passb已被替换为5仅发现我们在情况 2。在第二阶段!),并且必须在第二阶段检查所有内容。

两阶段查找的存在取决于被解释的标记及其在第一阶段选择的含义。另一种方法是像 VS 一样进行单遍查找。


*我在这里简化了案例,在 Visual Studio 编译器中,它不实现两阶段查找,b也可以是Base<T>当前实例化类型的成员T(即它可以是从属名称)

于 2012-10-16T02:15:18.127 回答
0

C++ 的许多优点是它是一种经过严格检查的语言。您尽可能清楚地表达程序的意图,编译器会告诉您是否违反了该意图。

我无法想象你会写Base<T>::Bar<false>(b);(来自 Dribeas 的例子)并且没有你想要的特定解释。通过将解释告诉编译器 ( Base<T>::typename Bar<false>(b);),如果有人提供具有static成员Bar或嵌套模板类型而不是成员函数模板的类型,它可能会生成有意义的错误。

其他语言旨在强调静态分析的简洁性;例如,许多动态语言都有大量“按我的意思做”的规则。当编译器将不合理的代码变成不可预测的东西时,这会很有趣,没有错误。(恰当的例子:Perl。我喜欢用它来处理文本,但天哪,DWIM 很烦人。几乎所有事情都是运行时错误,几乎没有任何静态检查可言)

于 2012-10-16T05:52:00.653 回答