【问题标题】:Pure functions in C++11C++11 中的纯函数
【发布时间】:2012-11-20 10:02:00
【问题描述】:

C++11 中的一个是否可以在 gcc 中以某种方式将函数(不是类方法)标记为 const,告诉它是 并且不使用全局内存而只使用它的论据?

我已经尝试过gcc__attribute__((const)),它正是我想要的。但在函数中触及全局内存时不会产生任何编译时错误。

编辑 1

请小心。我的意思是纯函数。 非恒定函数。 GCC 的属性有点混乱。纯函数只使用它们的参数。

【问题讨论】:

  • 不知道你的意思,但你试过constexpr
  • 不是这样的。我的意思是纯粹的不是恒定的。 GCC 的属性名称有点混乱。
  • 我认为你需要重新表述你的问题,因为你有正确的属性。它不会产生你想要的警告是另一回事 - 至少在你的问题表面上是这样。
  • @LucDanton 是的,你完全正确。但令我惊讶的是它没有产生任何警告或编译错误。而且我想知道是否有任何方法可以在编译时检查它。
  • 我开始感兴趣了。您能否解释一下您通常是如何做到这一点的,而不是在 C++11 中?

标签: c++ gcc c++11


【解决方案1】:

仅使用标准 C++11:

namespace g{ int x; }

constexpr int foo()
{
    //return g::x = 42;  Nah, not constant
    return 42;      // OK
}

int main()
{}

这是另一个例子:

constexpr int foo( int blah = 0 )
{
    return blah + 42;      // OK
}

int main( int argc, char** )
{
    int bah[foo(2)];            // Very constant.
    int const troll = foo( argc );  // Very non-constant.
}

GCC 的__attribute__( const ) 的含义在GNU compiler docs 中记录为……

许多函数除了参数之外不检查任何值,并且除了返回值之外没有任何效果。基本上这只是比下面的pure 属性更严格的类,因为函数不允许读取全局内存。

人们可能会认为这意味着函数结果应该只取决于参数,并且函数应该没有副作用。

这允许比 C++11 constexpr 更通用的函数类,这使得函数 inline,将参数和函数结果限制为 文字类型,并限制“活动”将函数体的语句转换为单个 return 语句,其中 (C++11 §7.1.5/3)

——用于初始化返回值(6.6.3、8.5)的每个构造函数调用和隐式转换都应是常量表达式(5.19)中允许的那些之一

举个例子,创建一个constexpr sin 函数很困难(我认为不是不可能,但很困难)。

但结果的纯度只对两方很重要:

  • 当已知为纯时,编译器可以省略具有已知结果的调用。
    这主要是对宏生成代码的优化。用inline 函数替换宏以避免愚蠢地生成相同的子表达式。

  • 当已知是纯调用时,程序员可以完全删除调用。
    这只是一个正确的文档问题。 :-)

因此,与其寻找一种方式来表达例如sin 在语言中,我建议避免通过宏生成代码,并记录纯函数。

并且将constexpr 用于实际可行的函数(不幸的是,截至2012 年12 月,最新的Visual C++ 编译器还不支持constexpr)。


之前有一个关于the relationship between pure and constexpr 的问题。主要是,每个constexpr 函数都是纯的,反之则不然。

【讨论】:

  • 不不不,我的意思是纯函数,不是常量函数。
  • @Martin:由于您的问题描述含糊不清,以至于到目前为止导致两个这样的答案,因此对答案投反对票有点粗鲁。 “有点”粗鲁?对不起,我的意思是,相当粗鲁。天哪。
  • @Martin:从您对“纯”的(模糊)描述看来,constexpr 是您正在寻找的东西。
  • @Martin:你是在建议那些可以帮助你的人花时间浏览,也许会发现 wtf。你的意思是?你是真的还是巨魔。等等,投反对票——你是个巨魔。
  • 干杯:如果我想喷人,我会说我希望微软的人知道“纯功能”是什么意思 ;-) 无意冒犯。
【解决方案2】:

您在寻找constexpr 吗?这告诉编译器可以在编译时评估该函数。 constexpr 函数必须具有文字返回和参数类型,并且主体只能包含静态断言、类型定义、使用声明和指令以及一个返回语句。可以在常量表达式中调用constexpr 函数。

constexpr int add(int a, int b) { return a + b; }

int x[add(3, 6)];

查看__atribute__((const)) 的含义后,答案是否定的,你不能用标准C++ 做到这一点。使用constexpr 将达到相同的效果,但仅限于更有限的一组功能。但是,只要编译后的程序行为相同(as-if 规则),编译器就可以自行进行这些优化。

【讨论】:

  • 谢谢。属性constexpr 几乎没用。也许让我们再等一会儿。也许其他一些gcc-specific 属性会有所帮助。
  • clang does detect and reject modificationsconstexpr 函数中的全局变量。 gcc 5.3 和 gcc6-snapshot 只是按照编写的方式编译函数。 IDK 是如何充分执行 constexpr 的,或者您是否可以通过它偷偷摸摸一些违规行为。请注意,C++14 更改了 constexpr 规则:C++14 有一个禁止事物的黑名单,而不是限制性白名单。
  • constexpr 函数不是纯函数
  • ... 纯函数不一定是 constexpr。这只是一个错误的答案
【解决方案3】:

因为这里已经提到了很多,所以让我们暂时忘记元编程,它无论如何都是纯函数式的,而且离题了。 然而,一个 constexpr 函数 foo 可以用非 constexpr 参数调用,在这种情况下 foo 实际上是一个在运行时评估的纯函数(我是此处忽略全局变量)。但是你可以写很多纯函数,你不能做 constexpr,这包括任何抛出异常的函数。

其次,我假设 OP 意味着 标记纯 作为供编译器检查的断言。 GCC 的 pure 属性正好相反,是 coder 帮助编译器的一种方式。

虽然 OP 问题的答案是否定的,但阅读尝试引入纯关键字(或 impure 并让 pure 成为默认)。

d-lang 社区很快发现“纯”的含义并不清楚。日志记录不应使函数不纯。在纯函数中应该允许不逃避函数调用的可变变量。具有不同地址的相同返回值不应被视为不纯。但 D 比纯洁更进一步。

因此 d-lang 社区引入了“弱纯”和“强纯”一词。但后来的争论表明,弱与强不是非黑即白,有灰色地带。见purity in D

Rust 很早就引入了“纯”关键字;他们因为它的复杂性而放弃了它。见purity in Rust

在“纯”关键字的巨大好处中,有一个丑陋的后果。模板化函数可以是纯函数也可以不是纯函数,具体取决于其类型参数。这会增加模板实例化的数量。这些实例化可能只需要暂时存在于编译器中,而不需要进入可执行文件,但它们仍然会增加编译时间。

语法高亮编辑器可以在这里提供一些帮助,而无需修改语言。优化 C++ 编译器确实会考虑函数的纯粹性,它们只是不能保证捕获所有情况。

我很遗憾这个功能的优先级似乎很低。它使对代码的推理变得更加容易。我什至会争辩说,它会通过激励程序员以不同的方式思考来改进软件设计。

【讨论】:

    猜你喜欢
    • 2014-01-18
    • 2016-10-13
    • 2012-12-02
    • 1970-01-01
    • 2023-03-27
    • 2017-09-09
    • 1970-01-01
    • 2023-03-29
    • 2013-01-18
    相关资源
    最近更新 更多