【问题标题】:Why can't templates be declared in a function?为什么不能在函数中声明模板?
【发布时间】:2011-08-27 07:20:02
【问题描述】:

阅读C++ Templates: The Complete Guide,上面写着

注意不能声明模板 在函数中

它没有对本书或外部资源中的任何其他章节进行解释和/或交叉引用。

谁能帮忙解释一下。可能会在本书后面解释,但还没有。如果前面解释过了,我一定是错过了。

例子:

int main()
{
  class DummyClass  //  This compiles ok
  {
    int object;
  };

  template <typename T> //  compile error "expected primary-expression before "template""
  class DummyTemplate
  {
    T object;
  };

  return 0;
}

我也不明白来自 gcc 的错误信息。错误消息说:

expected primary-expression before "template"

【问题讨论】:

  • 有几个答案基本上是说“你不能因为你不能”。有谁知道是否有充分的理由阻止这样做?
  • 模板参数必须有外部链接。至于为什么这是必要的,Greg Comeau 在这个 c.l.c++.moderated 讨论中提供了一些提示@groups.google.com/group/comp.lang.c++.moderated/browse_thread/…
  • @Abhay:这就是为什么函数范围内的类不能是模板参数,而不是模板声明不能处于函数范围内的原因。
  • @Mike Seymour:如果你知道确切的原因,为什么不给我们开导:)
  • @Tony:模板可能是一些函数对象,只能在这个函数中使用。我希望我能把这些本地化。

标签: c++ templates


【解决方案1】:

这意味着您不能执行以下操作

  void foo()
  {
       template <typename T> //Error
       T something;
  }

模板声明只允许在全局、命名空间或类范围内。 :)

背后的原因是什么?

这是不允许的,因为标准是这样说的。

ISO C++-98第 14.2 节

模板声明只能作为命名空间或类范围声明出现。

这有意义吗?

【讨论】:

  • 是的。谢谢。问题是为什么会这样。背后的原因是什么?
  • What is the reasoning behind it?。我对在函数中实现模板声明的技术了解不多,但标准不允许这样做。 ISO C++ (14.2)A *template declaration* can appear only as a namespace or class scope declaration:)
  • +1。谢谢。是的,这是有道理的。我看到标准不允许。这将有助于理解为什么。我已经看到有关标准允许(不允许)什么的问题,并通过一些解释来支持该标准。对我这样的学习者很有帮助。这绝不会从这个非常好的答案中带走任何东西。
  • 好吧,我也会反对在函数中声明结构/函数。我一直发现它比任何东西都更糟地弄乱了代码。无论如何,我认为这有助于使编译器编写者的生活更轻松,而且他们的工作已经足够艰巨,因为它不需要这种不会带来太多好处的“功能”。
  • “不允许,因为不允许”对于询问“为什么?”的问题是无效答案。也是一个逻辑谬误。
【解决方案2】:

究竟有什么用?所以你可以声明只能在函数中使用的模板变量吗?真的有用吗?

【讨论】:

  • 是的。我可以用结构和类来做到这一点。我能想到的只有一个是函数对象,它与函数中的算法一起使用的类/结构。不是必需的,但我这样做是为了让类/结构靠近我使用它们的位置。大概我可能有一些理由,但这更像是一种学习练习。我猜它可能没用的事实是不同的讨论。
  • 它的用处并不是真正的另一个讨论,IMO 它与这个非常相关。如果确实允许这样做,您将如何使用它?调用者看不到函数的内部结构,那么在调用该函数时如何以适当的类型实例化模板参数?
  • @Preatirian。我听到你了。但是,我可能不一定需要传递模板参数。我可以在函数中多次使用模板,使用不同的模板参数。我只是在这里大声思考学习:)
  • @Praetorian 抱歉名称拼写错误
  • @Praetorian:在 C++03 中,您不能使用本地类型实例化模板。但是,您可以将此模板的实例与非模板一起使用。 (例如,它可以派生自多态基类,并且您可以使用实例来调用获取该基类的引用/指针的函数。)
【解决方案3】:

为什么会这样,简短的回答是因为编写 c/c++ 编译器和标准的人希望它是这样的。函数内部的模板一定被认为太混乱和/或难以理解或解析,所以他们禁止这样做。

【讨论】:

  • 谢谢。非常容易理解,并且可以看出这很有意义。有参考吗?
  • @MeThinks:查看我对您的评论的评论。
【解决方案4】:

唯一有用的情况是,如果您使用一个函数创建了具有不同类型的模板的多个实例。无论如何,将您的私人课程移出您的功能。如果这开始让你的类变得混乱,那么它们就太大了,需要重构。

【讨论】:

  • 尽管如此,我同意这是一种奇怪的语言不一致。就像其他人所说的那样,只需将其归结为:“因为它就是这样。”
【解决方案5】:

我的猜测是它很难实现,这就是不允许它的原因(在标准 C++03 中)。另一方面,在函数之外编写类模板是一种可接受的解决方案。

【讨论】:

  • 自 C++14 以来已经在通用 lambdas 中实现:对象 [](auto x) {} 具有模板 operator(),只是没有命名模板参数。
【解决方案6】:

问题可能与模板的历史实现方式有关:早期的实现技术(有些至今仍在使用)要求模板中的所有符号都具有外部链接。 (实例化是通过在单独的文件中生成等效代码来完成的。)在函数内部定义的名称永远不会有链接,并且不能在定义它们的范围之外引用。

【讨论】:

  • 这应该是公认的答案,因为它至少试图给出背后的理由,而不仅仅是“因为标准是这样说的”。
【解决方案7】:

“因为标准这么说”的答案当然是正确的,但让我们考虑通用 lambda。

据我所知,在 C++14 和 C++17 中,泛型 lambda 是编写类似模板的代码的唯一方法:

    auto lambda = [](auto x) { };
    lambda.operator()<int>(0);

从技术上讲,您可以使用它编写任何类型的模板代码。尽管您必须努力解决此方法的各种限制。

不过,这在 C++20 中会更简单。使用通用 lambda 中的模板参数列表,您将能够编写如下代码:

    auto size = []<class T>() { return sizeof(T); };
    static_assert(4 == size.operator()<int>());

GCC 已经支持这种语法。

【讨论】:

  • 有趣。你能部分专门化泛型 lambdas 吗?
  • @ceztko 你不能部分专门化模板函数或方法(如果是通用 lambda,我们有模板方法 operator())。但是,您可以完全专门化 lambda 的 operator()(这是一个示例 godbolt.org/z/dspRsA),但您可能会发现 if constexprstd::is_same_v 更有用。
猜你喜欢
  • 2015-11-26
  • 2016-05-20
  • 1970-01-01
  • 2011-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多