代码之家  ›  专栏  ›  技术社区  ›  Aman Aggarwal Charlie

什么时候/为什么(如果有)我应该考虑做通用编程/元编程

  •  17
  • Aman Aggarwal Charlie  · 技术社区  · 15 年前

    在我看来,设计模式很有意义,而且我已经能够实际应用它们了。

    但说到 “通用编程/元编程” 对于现代C++类,我左右为难。

    --它是一种新的编程/设计范式吗?

    --是否仅限于“图书馆开发”?如果没有,那么使用元编程/通用编程需要什么样的设计/编码情况。

    --使用模板是否意味着我正在进行通用编程?

    我在这个话题上搜索了很多,但并没有完全把握全局。 也看到这个 post .


    在阅读了下面的讨论之后,直到现在,我确信(可能仍然不正确):

    a)一般编程和元编程是两个不同的概念。

    9 回复  |  直到 13 年前
        1
  •  30
  •   Stack Overflow is garbage    15 年前

    元编程是一个非常奇特的话题。学习起来很有趣,它是一个强大的工具,偶尔你会发现它很有用。但它永远不会是工具箱中最常用的工具。有时,您可能希望您的代码在一系列具有不同属性的不相关类型上运行,这就是元编程的切入点。通过一点小技巧,您可以编写一个函数的重载,该函数仅在参数类型为整数、指针或X、Y或Z类型(可能忽略Z上的常量)时才可用。

    它本质上是用类型编程。通常,您的程序可以做一些事情,比如取两个数字并产生第三个数字,或者告诉您一个数字是否满足某些要求。元编程可以采用两种类型并生成第三种类型,或者告诉您类型是否满足某些要求。是的,它可能在图书馆开发中最有用。但同样,大多数代码 能够 被视为图书馆代码。可以说,main()函数之外的所有内容都是库代码。

    通常,如果您想通过元编程解决一个问题,您可能需要使用相关的boost库来完成繁重的工作。Boost.typetraits当然还有Boost.mpl可以为你简化一些事情。但这不是你想要的 需要 要知道,这不是你经常需要的东西。

    通用编程是相关的(并且在某些情况下,可以使用引擎盖下的元编程来真正成为通用的,例如,标准库使用一点元编程将原始指针转换为有效的迭代器,“迭代器”概念需要通用的迭代器),但并不完全相同。它的应用也更为广泛。

    每次你实例化一个 std::vector ,使用通用编程。每次使用一对迭代器处理一系列值时,都使用通用编程。通用编程就是这样一种想法,即代码应该尽可能通用,并且应该能够工作,而不管将什么类型的代码放入其中。一个STD::vector不需要包含的类型来实现一个“IcBeNeNangOffice”接口(记住Java如何要求从对象派生的所有东西,以便将它存储在容器类中)?这意味着原始类型会被装箱,并且我们会失去类型安全性。这不是一般性的,而是毫无意义的限制。)

    使用迭代器在序列上迭代的代码是通用的,并且可以使用 任何 迭代器的类型,甚至使用普通指针。

    通用编程是非常有用的,并且通常可以在很大程度上取代OOP。(见上述示例。如果我能避免这种限制,为什么我要写一个容器,它需要所包含的类型来实现接口?)

    通常,在OOP中使用接口时,不允许在运行时更改类型(当然,这也会不时发生),而是允许您在编译时交换另一个类型(可能在测试期间插入一个模拟对象,而不是使用完整的实现),或者只是分离两个类。通用编程可以做到这一点,而不需要您做定义和维护接口的繁琐工作。在这些情况下,通用编程意味着您必须编写和维护更少的代码,从而获得更好的性能和更好的类型安全性。所以,是的,您应该对通用编程有一种宾至如归的感觉。C++不是一个很好的面向对象编程语言。如果你想严格遵守OOP,你应该切换到Java或其他更多的OOP固定语言。C++ 允许 您可以编写OO代码,但这通常不是最佳解决方案。几乎整个标准库都依赖于通用编程,而不是OOP,这是有原因的。标准库中几乎没有继承或多态性。他们不需要它,没有它,代码就变得更容易使用,更强大。

    回答你的其他问题,是的,通用编程基本上是一个独立的范例。模板元编程不是。它是操作类型系统的一种相当具体的技术,非常擅长解决少量的问题。要被认为是一个范例,我认为它必须更普遍地有用,而且基本上你可以使用的方法 一切 如函数、OO或通用编程。

    我认为Xtofl确实做到了这一点:通用编程就是让您的代码类型不知道。(标准::矢量没有 照顾 需要知道 存储在其中的类型。它只是工作。

    另一方面,元编程是关于类型计算的。给定类型t0和t1,我们可以定义类型t2,就像我们如何定义整数n0和n1一样,我们可以定义一个n2,它是n0和n1的和。

    mpl库有一个明显的例子。 在普通代码中,如果有整数n0、n1和n2,则可以创建包含这三个值的std::vector。然后我可以使用其他的算法来计算一个索引,然后提取存储在向量中那个位置的值。

    给定类型t0、t1和t2,我们可以创建一个包含这三种类型的mpl::vector 类型 . 我现在可以使用其他一些算法在编译时计算索引,并提取 类型 存储在矢量的那个位置。

        2
  •  11
  •   xtofl Adam Rosenfield    15 年前

    您真的需要区分通用编程(有点不了解类型)和元编程,这是在类型系统中进行计算的一种完全合法的方法。

    通用编程是 非常 在许多代码中找到可归纳模式时很有用。如果模式中的差异不仅存在于变量值中,而且也存在于不同的类型中,那么泛型编程对于重构代码非常有用。

    元编程适用于完全不同的领域。这个领域确实是非常新的,需要您自己进行一些探索。它 太有趣了!

    一个非常有用且常见的元编程模式是特性/策略概念。

        3
  •  9
  •   Roddy    15 年前

    C++模板元编程是一种强大的代码混淆技术,适用于多种应用领域:

    • 当你想写没有人能理解的代码时
    • 当你想要的代码在你写了7天后你就无法理解了
    • 当代码性能比可维护性更重要时
    • 当你想把“模板元编程”列为你的结果技能时。
    • 当您需要编写不太可能在许多实际编译器上工作的代码时
    • 如果你宁愿吃剃须刀片而不是使用预处理器宏

    另一种情况:

    • 如果你想知道Boost库是如何在引擎盖下工作的,或者你想为它们做出贡献。

    区分“泛型编程”(Stand Stl容器,AutoPyTrS等),这是C++模板设计完成的“模板元编程”(使用模板系统来让编译器有效地为您运行算法”是很重要的。

    我都赞成第一种观点,但很难看到第二种观点对现实世界的好处。

        4
  •  5
  •   Alexandru Nedelcu    15 年前

    对于高级模板和技术,我建议: Modern C++ Design ,安德烈·亚历山大·埃斯库

    C++是一种严格的语言,几乎没有运行时自省能力。您将遇到的许多问题都可以用模板来处理。此外,模板化语言是图灵完备的,使得在编译时生成复杂的数据结构和预先计算值成为可能。 在许多情况下,这可能比它的价值更麻烦。

        5
  •  3
  •   Tobias    15 年前

    什么时候?

    模板元编程是编写由编译器解释的程序的一种方法。它是一个更高的抽象层次。你可以用它来做各种奇怪和古怪的事情。Boost库包含许多示例:

    • 不喜欢C++是静态类型的吗?使用Booo::任何。
    • 喜欢LAMBDA演算,但你一定要C++吗?使用增压:lambda。
    • 有ebnf需要解析器吗?使用Boost::Spirit。

    为什么?

    很酷。你可以给约会对象留下深刻印象(也许)。

        6
  •  3
  •   Jacques Carette    14 年前

    一个尚未给出的答案是:尽管通用编程和元编程确实是彼此截然不同的技术,但它们都是为解决(本质上)相同的问题而设计的:如何摆脱样板文件。这些也体现在软件工程原理的两个首字母缩略词中: DRY (don't repeat yourself) and DIE (duplication is evil) . 这是现代对 Occam's Razor 也就是说,“实体不能无谓地成倍增加”。( 必须有一个非SUNT乘数 )[谁知道14世纪的逻辑学家能想出对21世纪软件设计有用的东西?]

    无论如何,这两种技术的要点都是找到将“几乎相同”的代码统一为一段参数化代码的方法。在元编程中,您使用的是代码生成,因此该技术涉及 专攻 特定情况下的一段通用代码(即模板,或更一般的代码生成函数)。在一般编程的情况下,您尝试使用类型系统来识别不同的代码片段是“相同的”,然后使用基于类型的分派进行专门化。

    事实证明,这两种技术甚至可以一起用来做一些很花哨的事情。见 Multi-stage programming with functors and monads: eliminating abstraction overhead from generic code 举个例子。但是如果你认为STL很可怕,最好不要看它…

        7
  •  1
  •   TimW    15 年前

    当你还有很长的时间想要 to play .

    当您需要增强算法,并且不希望虚拟函数调用产生开销时,可以用编译时绑定替换运行时绑定。

        8
  •  1
  •   Nick Dandoulakis    15 年前

    简单例子:

    template<typename T>
    void
    swap(T& var1, T& var2)
    {
      T var3 = var1;
      var1 = var2;
      var2 = var3;
    }
    
    int a = 2;
    int b = 3;
    swap(a, b);
    
    float c = 400.0f;
    float d = 500.0f;
    swap(c, d);
    

    交换具有相同类型的2个变量的值。

    使用模板,我们可以避免为各种类型显式地创建函数 组合 像这样:

    void
    swap(int& var1, int& var2)
    {
      int var3 = var1;
      var1 = var2;
      var2 = var3;
    }
    
    void
    swap(float& var1, float& var2)
    {
      float var3 = var1;
      var1 = var2;
      var2 = var3;
    }
    

    上面的函数将由编译器自动创建,在我的示例中,如果在代码中的某个位置调用swap()。 int 浮动 变量。

        9
  •  0
  •   Konstantin Tenzin    15 年前

    我认为,如果您不是库开发人员,那么最好忘记模板元编程。我认为,它在生产代码中是无用的,会带来更多的问题,然后带来好处:“一旦您开始使用它,那么您就失去了绑定到这个解决方案的灵活性。在开始使用模板煮沸的代码之后,很难摆脱它。这是一个问题,因为模板不够灵活”。

    当然,我不是指模板容器,交换…但亚历山德里斯科式的密码