【问题标题】:Relation between constexpr and pure functionsconstexpr 和纯函数之间的关系
【发布时间】:2011-03-28 16:56:39
【问题描述】:

我说的对吗:

  • 使用constexpr 定义的任何函数都是pure function,并且
  • 任何纯函数都可以而且必须用constexpr 定义,如果它对编译器来说不是很昂贵的话。

如果是这样,为什么不使用 constexpr 定义 <cmath> 的函数?

【问题讨论】:

  • 即使有宽松的constexpr 要求,<cmath> 函数也不能是constexpr,因为它们修改了全局变量errno

标签: c++ c++11 constexpr


【解决方案1】:

要补充其他人所说的内容,请考虑以下constexpr 函数模板:

template <typename T>
constexpr T add(T x, T y) { return x + y; }

constexpr 函数模板在某些情况下(例如,Tint)可用于常量表达式,但在其他情况下(例如,T 是具有operator+ 的类类型)未声明的重载constexpr)。

constexpr 并不表示该函数总是可用于常量表达式,而是表示该函数可能可用于常量表达式。

(也有类似的例子涉及非模板函数。)

【讨论】:

    【解决方案2】:

    除了前面的答案:函数上的 constexpr 限制 它的实现非常重要:它的主体必须对编译器可见 (内联),并且必须只包含一个 return 语句。我会成为 如果你能正确地实现 sqrt() 或 sin() ,你会感到惊讶 满足最后一个条件。

    【讨论】:

    • 哇,我不知道。谢谢和+1 :)
    • 我认为这样的事情是有效的:“constexpr int fact(int n) { return (n != 0 ? fact(n - 1) * n : 1); }” 所以虽然它可能用 constexpr 来实现 sqrt() 和 sin() 是很棘手的,我认为这当然是可能的。
    • @Clinton 7 你说得有道理:递归是允许的(故意的,还是疏忽的?)。因此,只要递归受输入参数的约束,就应该可以实现。 (例如,在两个近似值之间的差异小于给定值之前,您不能递归。)但它肯定不会像通常的 sqrt 或 sin 实现。
    • @James:原则上,您可以用递归来表达每个循环,并且可以使用函数参数“保存”状态,因此constexpr 函数集是图灵完备的。另见en.wikipedia.org/wiki/Church-Turing_thesis。作为一个有趣的说明,看看gitorious.org/metatrace,一个完整的编译时光线追踪器,除其他外,它还具有平方根计算以及光线/球体和光线/平面相交计算。但 sin/cos 并没有受到伤害。
    【解决方案3】:

    constexpr 函数不是pure,因为constexpr 是对编译器的一个提示,即该函数可能在编译期间被计算如果它的参数是常量 em> 函数体中提到的操作,对于这些参数,本身就是constexpr

    后者,使用模板代码,允许我们演示一个不纯的constexpr函数:

    template <typename T>
    constexpr T add(T lhs, T rhs) { return lhs + rhs; }
    

    用这种类型实例化

    DebugInteger operator+(DebugInteger lhs, DebugInteger rhs) {
      printf("operator+ %i %i", lhs._value, rhs._value);
      return DebugInteger(lhs._value + rhs._value);
    }
    

    这里,operator+ 不是 constexpr,因此可以读/写全局状态。

    我们可以说constexpr 函数在编译时被评估为pure...但就运行时而言它只是被一个常量替换。

    【讨论】:

    • 模板函数实际上是一个函数族,在你的例子中,任何不纯的家庭成员也必然不是 constexpr。
    • @HighCommander4:函数 isconstexpr(关键字)标记,在某些情况下它最终不是一个常量表达式。我会避免将 constant expression(艺术术语)与 constexpr(C++ 关键字)混为一谈。我的观点是,模板上的constexpr 是有效的,即使某些实例最终可能无法在常量表达式中使用。
    • @MatthieuM.:我不同意。你写了一个模板并在上面请求constexpr。在专业化后,编译器会尊重该请求,如果可以的话。你不能说函数是用 constexpr 标记的,因为函数模板不是函数。这就像说std::forward() 通过右值引用获取其参数,而std::forward() 的唯一目的是通过右值或左值引用获取参数,具体取决于模板参数。
    • @MarcMutz-mmutz:(不是 100% 确定,但是……)我认为正确的术语是实例化,而不是专业化。
    【解决方案4】:

    每个constexpr 函数都是纯函数,但不是每个纯函数都可以或应该是constexpr

    [涉及constexpr 函数模板的示例具有误导性,因为函数模板不是函数,它们是编译器生成函数的模式。函数模板的结果,它们的特化,函数,它们constexpr如果可能的话。]

    纯函数是仅依赖于其参数或其他常量状态的函数。这几乎就是constexpr 函数的含义。此外,constexpr 函数必须在首次使用之前定义(不仅是声明)(但似乎允许递归),并且必须仅包含 return 语句。这足以使允许的子集图灵完备,但结果不一定是运行时最有效的形式。

    这给我们带来了数学函数。您可能可以实现 constexpr sqrt()sin(),但它们必须使用编译器可以在编译时评估的递归实现,而在运行时,这些最好在一个汇编器操作中实现。由于constexprsqrt()sin() 的使用很少而且相距甚远,因此最好改为最大化运行时性能,这需要一个不是constexprable 的表单。

    你可能想知道为什么你不能编写一个函数的constexpr 版本和一个在运行时使用的函数,我同意这样会很好,但标准说你不能在 @ 上重载987654335@ness。也许在 C++17 中...

    【讨论】:

    • 并非每个constexpr 函数都是纯函数。一个 constexpr 函数可以做任何事情,只要存在一组导致纯执行的参数。
    • @RichardSmith:由于纯函数的定义是它只依赖于常量及其参数,并且不会引起可见的副作用,所以我看不出更改参数如何改变纯函数的一个功能。需要详细说明吗?
    • @mmutz 考虑int n; constexpr int f(bool b) { return b ? 0 : (n *= getc()); }。这是合法的,因为f 在给定参数true 时会生成一个常量表达式。但是f显然不是一个纯函数。
    • @Richard 如果参数为假,则该函数调用不是常量表达式。
    • @Ayxan 我同意,但这与问题无关。 f 是一个 constexpr 函数并且不是纯函数,因此并非所有 constexpr 函数都是纯函数。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-11
    • 1970-01-01
    • 2012-12-16
    相关资源
    最近更新 更多