当前位置: 首页 > 知识库问答 >
问题:

C模板实例化中依赖名称的查找

墨阳羽
2023-03-14

当我尝试编译这段代码时

// void foobar(int); 

template <class T>
struct Foo {
  void bar(T t) { foobar(t); };
};

void foobar(int);

template class Foo<int>;

对于g 4.8.2,我得到以下错误消息

foo.cc: In instantiation of ‘void Foo<T>::bar(T) [with T = int]’:
foo.cc:10:16:   required from here
foo.cc:5:27: error: ‘foobar’ was not declared in this scope, and no 
             declarations were found by argument-dependent lookup at 
               the point of instantiation [-fpermissive]
   void bar(T t) { foobar(t); };
                           ^
foo.cc:8:6: note: ‘void foobar(int)’ declared here, later in the translation unit
 void foobar(int);
      ^

(与3.4版本的叮当声几乎相同)。

首先,我认为代码是正确的,应该进行编译,因为foobar是模板声明中的一个依赖名称,应该仅在模板实例化的第二阶段进行查找。在最后一行中完成此操作时,已声明“foobar(int)”。顺便说一句,当我取消注释最上面的行时,代码会编译,但这两个声明都在实例化之前,所以这应该无关紧要。

其次,我觉得错误信息本身似乎是矛盾的。它说“在安装点没有发现任何声明”,这就是foo。抄送:10:16,上面说在foo宣布“稍后”。抄送:8:6。就我对数字和英语的所有了解而言,我称之为“之前”,而不是“之后”。

那么,这是gcc中的错误还是我做错了什么?然而,由于在我看来这似乎是一种常见的使用模式,我不能完全相信。

顺便说一句:当我在MSDN(http://msdn.microsoft.com/en-us/library/dx2zs2ee.aspx)上用g尝试“依赖类型的名称解析”的第二个示例时,结果与vc不同,这(不是一般的,但在这个特定的情况下)会破坏这是g中的一个错误。

共有3个答案

楮阳
2023-03-14

在我看来是正确的。虽然重载解析仅在第2阶段完成,但在第1阶段,您已经必须知道fobar(t)是一个函数调用表达式。如果fobar命名一种类型,t甚至不会是一个依赖名称。

邹开畅
2023-03-14

第二阶段查找仅包括不能在第一阶段应用的名称查找规则——例如ADL。两阶段查找正是这样——一些名称在第一阶段查找,一些在第二阶段查找。这种特殊的名称是第一阶段的名称,因为编译器完全能够在第一阶段在函数的命名空间中查找fobar

Visual C不实现两阶段名称查找。

沈弘文
2023-03-14

tl;dr<代码>Foo

首先,在这段代码中fobar是一个依赖的名称,因为(C 14/N3936)[temp.dep]/1

在形式的表达式中:

postfix-expression ( expression-list opt )

其中postfix-表达式是一个不合格的id,不合格的id表示一个依赖的名称,如果[...]

  • 表达式列表中的任何表达式都是类型相关表达式(14.6.2.2),或者

而t是一个依赖名称,因为它是声明的一部分,其中t是一个模板参数,因此是一个依赖类型。

转到依赖名称解析,有一个介绍了名称可以在定义上下文和实例化上下文中查找的事实,并定义了实例化上下文的位置。为了简洁起见,我省略了它,但在本例中,模板类Foo

下一位是[temp.dep.candidate]/1

对于后缀表达式为从属名称的函数调用,使用常用的查找规则(3.4.1、3.4.2)查找候选函数,但以下情况除外:

  • 对于使用非限定名称查找(3.4.1)的查找部分,只能找到模板定义上下文中的函数声明
  • 对于使用关联名称空间(3.4.2)的查找部分,只能找到在模板定义上下文或模板实例化上下文中找到的函数声明

最后两个部分是两阶段查找的“两阶段”。(注意——本节的措辞从C 11更改为C 14,但效果相同)。

在第一阶段3.4.1中,找不到foobar的名称。

所以我们进入第二阶段。按3.4.2所述查找名称的实际位置。文本很长,但以下是两条相关规则:

>

  • 如果T是基本类型,则其关联的命名空间和类集均为空。

    如果T是类类型(包括联合),则其关联类为:类本身;其所属类别(如有);及其直接和间接基类。其关联的名称空间是其关联类的最内层封闭名称空间。[...]

    所以当你实例化Foo时

    但是,如果您将示例更改为具有结构X{} 然后将int更改为X,这样代码就可以编译了。这是因为后一个要点:类类型参数的ADL会搜索该类的封闭命名空间(现在是全局命名空间),但内置类型参数的ADL不会搜索全局命名空间。

  •  类似资料:
    • 考虑以下示例: GCC和Clang都未能在C 17标准下编译此代码(使用Werror),因为(根据我的理解)在C 17 ADL中,当显式模板参数

    • 我有一些代码无法编译,我将其简化为以下最低版本: 在魔杖盒上看到 Clang(9.0.0)拒绝了这一点: Clang说是一个依赖的模板名称是对的吗?VS 2017没有问题。GCC(9.2.0)也拒绝该代码,但错误消息更加模糊: 按照叮当的建议改变冒犯的路线 修复Clang和GCC的编译。VS2017也接受此版本。 解决方案似乎很简单,但如果我重新排序函数调用: 或从 中删除参数: 错误消失了。 这

    • 这是关于cppreference的描述。com说 模板中使用的依赖名称的查找将推迟到模板参数已知时,此时[…]ADL检查从模板定义上下文或模板实例化上下文可见的具有外部链接的函数声明。 与此相反,以下代码段可以使用三个编译器(MSVC、clang、gcc)很好地编译: Foo是CallFoo中的从属名称:它取决于模板参数T。但是,尽管违反了上述两条规则,编译器还是找到了函数Foo。 从的定义或实例

    • 问题内容: 我正在尝试创建一个基本模板以显示所选实例的字段值及其名称。可以将其视为表格式的该实例的值的标准输出,在第一列中具有字段名称(如果在字段上指定,则为verbose_name),在第二列中具有该字段的值。 例如,假设我们具有以下模型定义: 我希望将其像这样在模板中输出(假定具有给定值的实例): 我要实现的目标是能够将模型的实例传递给模板,并能够在模板中动态地对其进行迭代,如下所示: 有没有

    • 问题内容: 我正在尝试创建一个基本模板以显示所选实例的字段值及其名称。可以将其视为表格式的该实例的值的标准输出,在第一列中具有字段名称(如果在字段上指定,则为),在第二列中具有该字段的值。 例如,假设我们具有以下模型定义: 我希望将其像这样在模板中输出(假定具有给定值的实例): 我要实现的目标是能够将模型的实例传递给模板,并能够在模板中动态地对其进行迭代,如下所示: 有没有一种经过批准的简洁方法?