Функция, объявленная как __attribute__ ((pure)), может возвращать только что созданный std :: string - PullRequest
0 голосов
/ 14 ноября 2018

В мире GNU C / C ++ с компилятором GCC существует Общий атрибут функции"pure" (который похож на атрибут "const", но с меньшими ограничениями):

Многие функции не имеют эффектов, кроме возвращаемого значения, и их возвращаемое значение зависит только от параметров и / или глобальных переменных.... Некоторыми распространенными примерами чистых функций являются strlen или memcmp.... Чистый атрибут накладывает аналогичные, но более слабые ограничения на определение функции, чем атрибут const: он позволяет функции читать глобальные переменные.... Поскольку чистая функция не может иметь побочных эффектов, для такой функции не имеет смысла возвращать 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

1 Ответ

0 голосов
/ 24 мая 2019

Эта документация была обновлена ​​или вы указали ее неправильно. Теперь часть, которую вы цитировали, говорит о «наблюдаемых побочных эффектах», а не о «побочных эффектах».

Что означает чистая функция:

Атрибут pure запрещает функции изменять состояние программы, которое можно наблюдать, кроме проверки возвращаемого значения функции.

сообщает GCC, что последующие вызовы [функции] с той же строкой могут быть заменены результатом первого вызова при условии, что состояние программы, наблюдаемой [функцией], между ними не изменится.

Таким образом, вопрос, который вы должны задать, заключается не в том, "является ли это законным", а в том, что "если второй вызов удален оптимизатором, изменится ли поведение моей программы". Если ответ «нет», вы можете использовать атрибут. Поведение чистых и константных функций не обеспечивается компилятором, это всего лишь подсказка оптимизатору.

Имеет ли возвращение std::string наблюдаемые побочные эффекты?

std::string выделяет память. Вы можете взять адрес внутреннего буфера и поместить его в глобальную переменную. В таком случае побочный эффект будет заметен.

Функция const не считывает глобальные переменные, поэтому этот побочный эффект не будет наблюдаться в самой функции. Но вы можете напечатать адрес внутреннего буфера после вызова функции и посмотреть, есть ли у вас два разных объекта.

Если бы вы использовали строку, как обычно, вместо попытки сломать вашу программу, побочный эффект не был бы заметен.

...