2

另一个是给语言律师的。

对于以下代码,无论是否存在显式实例化A<int>

clang 3.0 需要在andtypename的声明中,但不是.Type1Type3Type2

gcc 4.8.1 要求typename在声明中Type1,但不是在Type2orType3中。

struct C
{
    int f1();
    static int f2();
    int m;
};

template<int i>
struct S
{
    typedef int Type;
};

template<typename T>
struct B : C
{
};

template<typename T>
struct A : B<T>
{
    void f()
    {
        typedef typename S<sizeof(C::f1())>::Type Type1; // typename required
        typedef S<sizeof(C::f2())>::Type Type2; // typename not required
        typedef typename S<sizeof(C::m)>::Type Type3; // typename not required?
    }
};

template struct A<int>;

typename认为在声明中是必需的,Type1因为调用非静态成员的隐含对象参数是(*this),其类型是依赖的。参见[over.call.func]:

在非限定函数调用中,名称不由->or.运算符限定,并且具有更通用的主表达式形式。按照函数调用中名称查找的正常规则,在函数调用的上下文中查找名称。通过该查找找到的函数声明构成候选函数集。由于名称查找的规则,候选函数集由 (1) 完全由非成员函数或 (2) 完全由某个类 T 的成员函数组成。在情况 (1) 中,参数列表与调用中的表达式列表。在情况 (2) 中,参数列表是调用中的表达式列表,通过添加隐含的对象参数来扩充,就像在限定的函数调用中一样。如果关键字this在范围内并引用类T,或 的派生类T,则隐含的对象参数是 (*this)

遵循这个逻辑,typename在声明中不需要,Type2因为成员是静态的。声明中也不typename需要,Type3因为表达式不是对非静态成员的调用。

我已经阅读了这篇文章:我必须在哪里以及为什么要放置“模板”和“类型名”关键字? .. 并通过 [temp.dep.type] 和 [temp.dep.expr] 中的规则。在确定表达式是否依赖时,我看不到任何指定是否应对非静态成员函数名称进行特殊处理的内容。标准是否规定了这一点?

编辑:删除了关于转换为基于 [class.mfct.non-static] 的类成员访问表达式的讨论 - 凯西的回答更详细地讨论了这一点。

4

2 回答 2

3

C是在第 1 阶段查找期间通过非限定名称查找解析的非依赖名称。C::f1并且C::f2是在模板查找的第一阶段也通过限定名称查找解析的非依赖名称。无论是否CA.

问题中引用的 [class.mfct.non-static] 的文本似乎来自 C++03。C++11 文本 (9.3.1/3) 内容如下:

当不属于类成员访问语法 (5.2.5) 并且不用于形成指向成员 (5.3.1) 的指针的id 表达式X(5.1) 用于类成员的上下文中this时使用 (5.1.1),如果名称查找 (3.4) 将id-expression中的名称解析为某个 class 的非静态非类型成员C,并且如果id-expression可能被评估[emph. mine] 或CisX或 的基类X,使用(9.3.2) 作为运算符左侧的后缀表达式将id 表达式转换为类成员访问表达式 (5.2.5) 。(*this).

C::f1可能在可以使用的上下文中进行评估,因此转换为. 由于显式依赖于模板参数,因此是14.6.2.1/5中未知专业化的成员。[该标准使用术语“未知专业化的成员”来表示可以指示模板的某些专业化的成员的表达式,但不能证明对于所有专业化都是如此。]thisC::f1(*this).C::f1B<T>(*this).C::f1

由于(*this).C::f1表示未知专业化的成员,因此它是根据 14.6.2.2/5 的类型相关的类成员访问表达式。通过扩展,(*this).C::f1()根据 14.6.2.2/1 取决于类型。 sizeof((*this).C::f1())然后根据 14.6.2.3/2取决于值。S<sizeof((*this).C::f1())>-具有值相关参数表达式的simple-template-id - 然后是每个 14.6.2.1/8 的依赖类型。

既然S<sizeof((*this).C::f1())>是依赖,

S<sizeof((*this).C::f1())>::Type

根据 14.6.2/1 “...如果id 表达式非限定 ID模板 ID,其中任何模板参数都依赖于模板参数。” 最后——但同样重要的是,14.6/2 要求使用关键字来指示从属名称指的是一种类型:typename

typename S<sizeof((*this).C::f1())>::Type

认为这就是完整的推理链。

编辑:这个答案似乎是错误的。短语“可能评估”在 3.2/2 一个定义规则 [basic.def.odr] 中定义:

一个表达式可能被求值,除非它是一个未求值的操作数(第 5 条)或其子表达式。

C::f1在这个例子中肯定是一个子表达式,C::f1()它是一个未计算的操作数,sizeof每个 5.3.3/1 Sizeof [expr.sizeof]。

编辑:我相信表达式qualified-id()当前不是但应该被认为是类型相关的,当(1)它出现在类模板的成员函数中并且(2)限定ID是一个非依赖名称,表示重载集某些类的非静态成员函数和 (3) 的类型this是依赖的。确定表达式的类型需要根据 13.3.1.1.1/3 进行重载决议,如果this在范围内而不确定所引用的类的类型,这是不可能的this

我认为这可以通过向 14.6.2.1/5 [temp.dep.type] 添加一个项目符号来实现,内容如下:

  • 在非限定函数调用 (13.3.1.1.1/3 [over.call.func]) 中使用的限定 ID,表示某个类的一组成员函数,该类T不是当前实例化或非依赖基类,当当前实例化至少有一个依赖基类时。
于 2013-07-15T07:11:11.837 回答
2

我不完全确定,但这里有一个使用你的报价的解释:

  • C::f2只是一个普通函数,所以它是无条件已知的,并且sizeof(C::f2())不依赖于模板参数T——它只是一个普通类型,比如S<6>.

  • C::f1()实际上是this->f1(),并且由于this依赖于模板参数TS<sizeof(this->f1())>是依赖类型,因此需要消歧。

于 2013-07-14T19:05:47.513 回答