【问题标题】:creating a containor iterator inside a function template在函数模板中创建容器迭代器
【发布时间】:2012-08-03 01:27:41
【问题描述】:

代码是使用 GCC 编译的。这项工作在 VC++ 中没有任何错误

template <typename T>
void Function(T& A){

  T::iterator it; //Error : dependent-name 'T::iterator' is parsed as a non-type,
                  //but instatiation yields a type.
}

This article 指出编译器无法确定T 类型中的迭代器是类还是静态成员。所以我们必须使用typename关键字来将符号分类为一个类型。

我的问题是,因为 T 在编译时是已知的,那么编译器已经知道 T 内部的 iterator 是一个类(在我的例子中,T 是 vector&lt;int&gt;)。那么为什么会出现错误呢?

这也是typename 关键字的另一种用法,除了将其用作定义模板参数T

更新:

我阅读了 here 的所有答案和其他答案,它们确实回答了我的所有想法。我可以总结为:

处理这个权利的正确编译器是 Gcc。 VC++ 将允许您编译格式错误的代码。使用 Gcc 编译时出现的错误是由于语法分析造成的,因为 Gcc 会尝试解析函数模板的代码,但会发现语法错误 T::iterator it;,因为 Deafault 的 Gcc 将 T::iterator 视为变量(T::iterator 被解析为非类型)而不是类型,要解决此问题,您必须明确告诉 Gcc 将 T::iterator 视为类型,这是通过添加关键字 typename 来完成的。
现在回到 VC++。为什么这个工作的答案是因为 VC++ 中存在的错误,VC++ 是否延迟了T::iterator 是变量还是类型的决定。或者 VC++ 在它认为需要的地方提供关键字 typename


Useful Article
注意:如果您发现不正确的内容,请随时编辑更新。

【问题讨论】:

  • 你能提供文章的链接吗? this onethis other one 怎么样?
  • 在 VS2010 中编译并运行良好。
  • @Aesthete :是的,我忘了提到它可以与 VC++ 和一些编译器一起使用,但在 Gcc 和其他编译器中,不会工作。
  • @AlexDan 这是一篇很长的文章。你指的是那篇文章的哪个地方?

标签: c++ templates iterator


【解决方案1】:

您参考的文章提供了解释:

必须告诉编译器指定的符号实际上是一个类型,而不是给定类的静态符号。

考虑一个来自this article的例子:

class ContainsAType {
   class iterator { ... }:
   ...
};

class ContainsAValue {
   static int iterator;
};

所以在您上面的Function() 中,编译器必须知道T::iterator 是类型还是静态变量。 typename 关键字消除了 C++ 中的歧义。

【讨论】:

  • 当我调用“Function(ob)”并且“ob”是“vector”类型时,编译器知道“T”是“vector”和“ T::iterator" 是一个类而不是静态变量。那么这里的歧义在哪里?
  • @AlexDan C++ 解析器 不知道T 是一个向量。想象一下为模板化函数构建抽象语法树
  • 在模板函数“Function >”中没有“T”类型,它已经被“vector”替换了。所以你可以构建解析树,如果我错了,请纠正我。
  • @AlexDan Function() 的 AST 没有实例;它只是有模板,如果你愿意的话,它充当占位符。模板实例化在编译的语义分析阶段才会发生。
  • @AlexDan C++ 需要两阶段名称查找。一次是模板定义时间,一次是模板实例化时间。为了使第一种情况起作用,必须说明从属名称是类型还是值,因为缺少要推断的信息。 MSVC 根本不符合,因此它似乎神奇地工作。
【解决方案2】:

This thread 包含一些关于在类似案例中使用 typename 的很好的讨论。

我的问题是,由于 T 在编译时是已知的,那么编译器已经知道 T 内部的迭代器是一个类(在我的情况下,T 是向量)。

是的,这就是 VC++ 可以不用 typename 关键字的原因。

那么为什么会出现错误呢?

因为标准规定模板名称查找应分两个阶段完成。在某些情况下,这需要消除歧义 (see this)。因此错误出现在 GCC 中,因为它符合两阶段名称查找的标准。另一方面,VC++ 使用后期解析方案。

正如 DeadMG 所指出的,当编译器无法诊断丢失的类型名时,VC++ 的方法可能会因更复杂的代码而失败。

也请查看this thread

【讨论】:

  • 有时语言设计决策是为了编译器实现者而做出的。
  • VC++ 通过在两阶段查找方面不符合标准来“解决”这个问题。这可能会在更复杂的模板代码中产生问题。
  • 你能给我一个 VC++ 有问题的模板代码示例吗?我已经看到其他地方提到了失败的可能性,但我无法找到有关导致失败的情况的任何详细信息。
【解决方案3】:

当编译器看到T::iterator it; 时,它还不知道T 是什么。它只会在您调用函数时知道T 是什么(实例化时间)。这是两阶段名称查找的一部分,在第一阶段,编译器检查Function 的声明是否存在语法错误并查找所有非依赖名称。这有助于在定义点检测错误,而不是等到实例化点。

例如:

template <typename T>
struct A
{
    X x;
};

struct X{};

int main()
{
    A<int> a;
}

一个好的编译器会查找X 并告诉你它不知道它是什么。但是,MSVC 编译器会接受这种格式错误的代码。

这是另一个例子:

template <typename T>
class A : public I_do_not_exist
{
};

一个好的编译器会告诉你I_do_not_exist 不存在(MSVC 编译此代码)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-01-09
    • 1970-01-01
    • 2015-12-02
    • 1970-01-01
    • 2014-10-05
    • 1970-01-01
    • 1970-01-01
    • 2012-01-20
    相关资源
    最近更新 更多