【问题标题】:is c++ Template Metaprogramming a form of functional programmingc++模板元编程是函数式编程的一种形式吗
【发布时间】:2012-07-12 19:19:57
【问题描述】:

c++ 模板元编程是函数式编程的一种形式吗?如果是,是否存在一些陷阱,例如与 c++ 模板元编程相关的非尾递归的 stackoverflow?

this question 中的阶乘模板示例,我猜它是标准函数式编程。还是相似只是表面现象?

#include <iostream>
using namespace std;

template< int n >
struct factorial { enum { ret = factorial< n - 1 >::ret * n }; };

template<>
struct factorial< 0 > { enum { ret = 1 }; };

int main() {
    cout << "7! = " << factorial< 7 >::ret << endl; // 5040
    return 0;
}

【问题讨论】:

  • C++ 模板实际上是一种极其纯粹的函数式编程语言(没有副作用!),与大多数函数式语言不同,它的特点是完全在编译时进行评估。而且语法不是很好。因此,与一个工作的相同类型的技术和算法也映射到另一个是有道理的。事实上,存在将 Haskell 的一个子集“脱糖”为 C++ 模板的项目。我不太了解性能特征/“操作语义”。
  • 也许你应该看看boost::phoenix
  • 这绝对是一个真实的问题。这只是一个有点深奥的。
  • @Marcin :“不是一个真正的问题”的描述为“这个问题模棱两可、含糊不清、不完整、过于宽泛或修辞,无法在其目前的形式。” -- 我想说的不止适用。
  • @ildjarn 这个问题有多宽泛?非常具体。

标签: c++ templates functional-programming


【解决方案1】:

我认为最好的答案是这两个概念是完全不同的东西。然而,这并没有真正的帮助,所以这里有一些我能想到的关键点:

  • C++ 模板和(参数多态性)函数式语言(如 ML)都是 generic programming 的示例,因此在一个问题中一起提及它们是有意义的。在学术界,甚至有 Workshop on Generic Programming 经常涵盖 C++ 模板内容和 ML 风格的函数式编程。

  • 但是,模板元编程为您提供了一种编写代码的方法,该代码在 C++ 代码编译期间得到“评估”,因此您只能将其用于非常有限的任务。另一方面,函数式程序通常在运行时运行,您可以定义复杂的数据结构、构建抽象等(您可以使用模板元编程做一些事情,但它看起来很丑)。

  • 为什么阶乘示例与函数式编程中的阶乘相似?当您使用模板元编程时,您无法定义“可变变量”(因为您只是在定义新类型或模板),因此模板元编程可能感觉有点像函数式编程风格。

  • 使用模板元编程编写的代码在编译时进行评估。这意味着它不能依赖于用户输入!它要么编译,要么不编译(编译器可能对递归深度有一些限制,并在一段时间后放弃)。另一方面,函数式程序可以接受一些输入然后进行评估(这意味着它们可以使用所有可用的堆栈或内存然后失败)。

对于函数式程序员,我知道像Agda 这样的一些函数式语言也可以在编译时评估事物,但我认为最好保持简单!

【讨论】:

  • 它们不是完全不同的东西。说它们是同一件事会更接近事实:C++ 模板元编程是函数式编程。 (虽然反过来,当然不是真的。)
  • 关键点与您的说法相矛盾:c++ 模板元编程和函数式编程不一样,但非常接近。事实上,您可以在 Haskell(的子集)中解析函数式程序,转换甚至在 C++ 编译期间执行它。例如。在abel.web.elte.hu/mpllibs/metaparse/index.html 看到一个图书馆可以做到这一点。
  • 你们都提出了一些好的观点,但我仍然会说它们在高层上是完全不同的东西。在低级别,它们共享一些技术方面(例如模板元编程使用功能样式的子集),但如果您想考虑全局,这并不是真正有用的信息。
  • 在基础上,函数式编程是完全由无副作用评估组成的编程,它完美地描述了模板元编程。寻找诸如用户输入之类的功能是无关紧要的。一些函数式语言(例如 lambda 演算)不接受用户输入。
【解决方案2】:

c++ 模板元编程是函数式编程的一种形式吗?

是的!模板扩展没有副作用,所以是纯函数式的。

如果是,是否存在一些类似于 c++ 模板元编程相关的非尾递归的 stackoverflow 陷阱?

当然。阶乘并不是一个很好的证明,因为结果会在你的堆栈溢出之前很久,但是长递归肯定会导致编译器出错。然而,有趣的是,编译器倾向于以这样一种方式实现模板,即您可以获得自动记忆。例如,斐波那契数列的简单编写实现倾向于在 O(n) 时间内编译,而不是 O(2^n)。

【讨论】:

    【解决方案3】:

    模板元编程在理论上是一种纯粹的面向函数的语言,即函数只依赖于它们的参数,而不管任何全局或局部状态。但是,我们似乎可以使用友元注入来捕获和检索元编程状态。因此,函数可以根据全局/局部元编程状态返回不同的结果。

    【讨论】:

    • 然而,检索元编程状态并非有意为之,而且根据CWG 2118 的说法,从 ISO 委员会的观点来看,“这很神秘,应该被制成不正确的格式”。
    猜你喜欢
    • 2017-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-28
    • 2010-12-19
    • 2013-07-24
    • 1970-01-01
    • 2011-04-27
    相关资源
    最近更新 更多