被声明为__attribute__((pure))的函数是否允许返回新构造的std :: string

时间:2018-11-14 17:18:46

标签: c++ gcc function-attributes

在带有GCC编译器的C / C ++的gnu世界中,有Common function attribute“纯”(类似于“ const”属性,但限制较少):

  

除返回值外,许多函数均无效,它们的返回值仅取决于参数和/或全局变量。 ...纯函数的一些常见示例是strlen或memcmp。 ... pure属性对函数的定义施加了与const属性类似但较宽松的限制:它允许函数读取全局变量。 ...因为纯函数不能有任何副作用,所以使此类函数返回void没有意义。

是否可以使用纯函数来调用任何std::stringstd::vector之类的C ++ STL构造函数?例如,此代码合法,为什么不合法? (使用__attribute__((const))是否合法?)

#include <string>
#include <cstdio>
__attribute__((pure)) std::string GetFilesystemSeparator(int unixvar) {
   if(unixvar) {
      return "/";   
   } else {
      return "\\";
   }
}

int main() {
    std::string dirname1="dir1";
    std::string dirname2="dir2";
    std::string filename="file";
    int unixvar;
    std::string path;
    puts("Unix style:");
    unixvar = 1;
    path=dirname1 + GetFilesystemSeparator(unixvar) + dirname2 +  GetFilesystemSeparator(unixvar) + filename;
    puts(path.c_str());


    puts("Not Unix style:");
    unixvar = 0;
    path=dirname1 + GetFilesystemSeparator(unixvar) + dirname2 +  GetFilesystemSeparator(unixvar) + filename;
    puts(path.c_str());
    return 0;
}

g++ pure.cc -o pure -fverbose-asm --save-temps
clang++ pure.cc -o pure1 -O3 -save-temps

对复杂的std :: sting构造函数有一些调用,该构造函数可以分配内存并写入一些用于管理可用内存和已分配内存的全局变量:

less  pure.s
...
_Z22GetFilesystemSeparatorB5cxx11i:
    call    _ZNSaIcEC1Ev@PLT        #
    call    _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1EPKcRKS3_@PLT    #

例如,将"/""\\"常量的长度更改为100个字符之后,我从构造函数中调用了newmalloc(101)

ltrace -e '*@*' ./pure3
...
libstdc++.so.6->strlen("////////////////////////////////"...)                         = 100
libstdc++.so.6->_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_M_constructIPKcEEvT_S8_St20forward_iterator_tag(0x7ffc7b66a840, 0x558899f74570, 0x558899f745d4, 0 <unfinished ...>
libstdc++.so.6->_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9_M_createERmm(0x7ffc7b66a840, 0x7ffc7b66a6b0, 0, 0 <unfinished ...>
libstdc++.so.6->_Znwm(101, 0x7ffc7b66a6b0, 0, 0 <unfinished ...>
libstdc++.so.6->malloc(101)                                                           = 0x55889bef0c20
<... _Znwm resumed> )                                                                 = 0x55889bef0c20
<... _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9_M_createERmm resumed> )   = 0x55889bef0c20
libstdc++.so.6->memcpy(0x55889bef0c20, "////////////////////////////////"..., 100)    = 0x55889bef0c20

1 个答案:

答案 0 :(得分:0)

此文档已更新或您引用不正确。现在,您引用的部分谈论的是“可观察到的副作用”,而不是“副作用”。

纯函数的含义:

  

pure属性禁止函数修改程序的状态,而不能通过检查函数的返回值来观察该状态。

     

告诉GCC,如果在[函数]可以观察到的程序状态之间没有变化,则可以用第一次调用的结果替换随后对具有相同字符串的[函数]的调用。

因此,您应该问的问题不是“这合法吗”,而是“如果优化程序删除了第二个调用,我的程序的行为是否会发生变化”。如果答案为“否”,则可以使用该属性。纯函数和const函数的行为不是由编译器强制执行的,它只是对优化器的提示。

返回std::string是否有明显的副作用?

std::string分配内存。您可以获取内部缓冲区的地址,并将其放入全局变量中。在这种情况下,副作用是可以观察到的。

const函数不会读取全局变量,因此在函数本身中这种副作用是无法观察到的。但是您可以在调用该函数后打印内部缓冲区的地址,并查看是否有两个不同的对象。

如果像平常一样使用字符串,则不会观察到副作用,而不是试图破坏程序。