为什么不能在函数内声明模板?

30

我正在阅读《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"

5
有几个答案基本上都说“你做不了因为你做不了”。有人知道是否有一个好的理由阻止这样做吗? - Mike Seymour
1
模板参数必须具有外部链接。为什么这是必要的,格雷格·科莫在这个c.l.c ++.moderated讨论中提供了一些提示@ http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/f822a008746d1e68/2e9e48a43743e9e1?lnk=gst&q=why+local+templates#2e9e48a43743e9e1 - Abhay
2
@Mike Seymour:如果你知道确切的原因,为什么不给我们启示呢 :) - Prasoon Saurav
1
你为什么会考虑去做这样的事情呢? - Tony The Lion
6
@Tony:这个模板可以是一个函数对象,仅在此函数内部使用。我希望我能够将它们设为局部变量。 - sbi
显示剩余12条评论
7个回答

57

问题可能与模板的历史实现方式有关: 早期实现技术(一些至今仍在使用)要求模板中的所有符号都具有外部链接性。 (通过生成相应代码的独立文件来进行实例化。) 函数内定义的名称从不具有链接性,不能在定义它们的作用域之外引用。


10
这应该成为被接受的答案,因为它至少试图给出其中的原因,而不仅是“因为标准是这样规定的”。 - Super-intelligent Shade

8

答案“因为标准规定如此”当然是正确的,但让我们考虑通用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已经支持这种语法。


有趣。你可以对通用lambda进行部分特化吗? - ceztko
@ceztko,你无法部分特化模板函数或方法(在泛型 Lambda 的情况下,我们有模板方法operator())。但是,你可以完全特化 Lambda 的operator()(这里有一个例子https://godbolt.org/z/dspRsA),但你可能会发现`if constexprstd::is_same_v`更有用。 - D. Dmitriy

1
为什么会这样的简短回答是因为编写C/C++编译器和标准的人想要它成为这样。函数内部的模板可能被认为过于混乱和/或难以理解或解析,因此被禁止使用。

谢谢。非常易懂,可以看出很有道理。有没有参考资料? - dubnde
@MeThinks:看看我对你的评论的回复。 - Prasoon Saurav

0

这意味着你不能做如下操作

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

模板声明只允许在全局、命名空间或类作用域中。 :)

这背后的原因是什么?

不允许是因为标准规定如此。

ISO C++-98 (第14.2节)

模板声明只能出现在命名空间或类作用域中。

这有道理吗?


7
好的,理解了。问题是为什么会这样,背后的原因是什么? - dubnde
3
这个问题的推理背后是什么?我不太了解在函数内部实现模板声明的技术细节,但这在标准中是不被允许的。ISO C++(14.2)说:模板声明只能出现在命名空间或类作用域声明中 :) - Prasoon Saurav
7
+1。谢谢,我明白了。我看到标准不允许这样做。但是理解为什么会有所帮助。我曾经看过一些问题关于标准允许或不允许的解答,并附有一些支持标准的解释。对像我这样的学习者非常有帮助。这并不会减少这个很好的回答的价值。 - dubnde
好吧,我也会反对在函数内部声明结构体/函数。我一直觉得这会让代码变得更混乱。不管怎样,我想这有助于让编译器编写者的工作更轻松,他们已经很辛苦了,不需要这种没有多大用处的“功能”。 - Matthieu M.
考虑到在C++中,函数局部变量与其他作用域中的相同变量(生成的代码方面)是相同的,我预计它们对编译器几乎没有任何成本。 - BCS
3
“因为不允许”不是针对“为什么?”问题的有效回答,同时也是一种逻辑谬误。 - Calmarius

0

我猜实现起来很难,这就是为什么它在标准C++03中不被允许的原因。另一方面,在函数外编写类模板是一个可接受的解决方案。


1
这已经在C++14中由泛型lambda实现了:对象[](auto x){}具有模板operator(),它只是没有命名模板参数。 - D. Dmitriy

-1

这有什么用处呢?所以你可以声明模板变量,只能在函数内部使用?这真的有用吗?


它的实用性并不是另一个讨论,我认为它与这个讨论非常相关。如果确实允许,你将如何使用它?函数的内部对调用者不可见,因此在调用此函数时如何实例化模板参数以使用适当的类型? - Praetorian
1
@Preatirian。我听到你的意见了。然而,我不一定需要传递模板参数。在函数中,我可以多次使用模板,并使用不同的模板参数。我只是在这里大声思考学习 :) - dubnde
@Praetorian 对不起,我的名字拼错了。 - dubnde
@Praetorian:在C++03中,您无法使用本地类型实例化模板。但是,您可以使用非模板的此模板实例。 (例如,它可以从多态基类派生,并且您可以使用实例调用函数,该函数接受对该基类的引用/指针。) - sbi
它可以用于仅需要由一个函数进行比较和检查的情况。由于C++11允许将本地类型用作模板参数,因此可以在函数内部使用template<typename T> struct ComparatorTemplate { using type = T; };或必要的专业变体,从而将它们隐藏在外部代码之外。 - Justin Time - Reinstate Monica
显示剩余2条评论

-2
这种情况只有在您在一个函数中创建多个不同类型的模板实例时才有用。无论如何,将私有类移出函数。如果这开始使您的类变得混乱不堪,那么它们就太大了,需要进行重构。

2
尽管如此,我同意这是语言中奇怪的不一致之处。就像其他人说的那样,只能把它归结为:“因为它就是这样。” - Cirdec

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接