在带有 GCC 编译器的 C/C++ 的 gnu 世界中有 Common function attribute “纯”(类似于“常量”属性,但限制较少):
Many functions have no effects except the return value and their return value depends only on the parameters and/or global variables. ... Some common examples of pure functions are strlen or memcmp. ... The pure attribute imposes similar but looser restrictions on a function’s defintion than the const attribute: it allows the function to read global variables. ... Because a pure function cannot have any side effects it does not make sense for such a function to return void.
是否允许纯函数调用任何 C++ STL 构造函数,如 std::string
或 std::vector
?例如,这段代码是否合法,为什么不合法? (使用 __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 个字符后,我有 new
和 malloc(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
最佳答案
此文档已更新或您引用不正确。现在你引用的部分说的是“可观察到的副作用”,而不是“副作用”。
什么是纯函数:
The pure attribute prohibits a function from modifying the state of the program that is observable by means other than inspecting the function’s return value.
tells GCC that subsequent calls to [the function] with the same string can be replaced by the result of the first call provided the state of the program observable by [the function] does not change in between.
所以你应该问的问题不是“这是合法的”,而是“如果优化器删除了第二次调用,我的程序的行为是否会改变”。如果答案为“否”,则可以使用该属性。 pure 和 const 函数的行为不是由编译器强制执行的,它只是对优化器的提示。
返回 std::string
是否有明显的副作用?
std::string
分配内存。您可以获取内部缓冲区的地址,并将其放入全局变量中。在这种情况下,可以观察到副作用。
const 函数不会读取全局变量,因此在函数本身中不会观察到这种副作用。但是你可以在调用函数后打印内部缓冲区的地址,看看你是否有两个不同的对象。
如果您像往常一样使用字符串,而不是试图破坏您的程序,则不会观察到副作用。
关于c++ - 声明为 __attribute__ ((pure)) 的函数是否允许返回新构造的 std::string,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53305586/