Соглашение о связях C/C++

При вызове алгоритмов C++, таких как copy_if, transform и т. Д., Которые в качестве последнего аргумента принимают унарную или двоичную функцию, могу ли я передать функцию библиотеки C, например, atoi или tolower.

Например, ниже звонки работают нормально и дают правильный вывод (пробовал в ideone)

1) transform (foo, foo+5, bar, atoi);
2) transform (foo, foo+5, bar, ptr_fun(atoi));
3) transform(s.begin(),s.end(),s.begin(), static_cast<int (*)(int)>(tolower));

Гарантируется ли это использование со всеми компиляторами C++?

В книге "Размышление на С ++" упоминается "Это работает с некоторыми компиляторами, но это не обязательно". Упомянутая причина (как я понимаю), transform - это функция C++ и ожидает, что ее последний аргумент будет иметь то же соглашение о вызовах.

В книге также предлагается решение этой проблемы, которое заключается в создании такой функции-обертки в отдельном файле cpp и не включает заголовочный файл iostreams.

// tolower_wrapper.cpp
string strTolower(string s) {
  transform(s.begin(), s.end(), s.begin(), tolower);
  return s;
} 

Это работает нормально, но я не понял, как это решает проблему соглашения о вызовах? transform по-прежнему является функцией C++, а tolower по-прежнему является функцией C в strTolower, так как здесь обрабатываются различные соглашения о вызовах.

1 ответ

Решение

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

Указатель на функцию - это просто указатель на функцию, которая ожидает получить определенный набор параметров и вернуть определенный тип.

Функциональный объект является экземпляром класса, который имеет переопределенный оператор ().

При расширении шаблона алгоритма компилятор сможет увидеть, какой из двух случаев применим, и сгенерирует соответствующий код вызова.

В случае использования функции C в качестве двоичной функции в алгоритме это указатель функции, который вы предоставляете. Вы можете вызывать функцию C из C++, если она объявлена extern C { ... },

Многие компиляторы поставляются с заголовочными файлами для функций библиотеки C, которые включают что-то вроде этого:

#ifdef  __cplusplus
extern "C" {
#endif

/* function declarations here */

#ifdef  __cplusplus
}
#endif

так что если вы включите заголовок библиотеки C из программы на C++, все содержащиеся в ней функции будут волшебно доступны для вас. Однако эта часть не гарантируется стандартом, поэтому ваша книга утверждает, что она может работать не со всеми компиляторами.

Другой недостаток заключается в том, что вам не разрешено приводить указатели функций к типу с другой языковой связью, что, по крайней мере, в некоторых ваших примерах вы делаете, хотя некоторые компиляторы, кажется, в любом случае допускают это - например, смотрите эту ошибку GCC.

Другой улов, который относится конкретно к tolower Например, некоторые имена функций библиотеки C также являются именами функций или шаблонов в библиотеке C++ std. Например, имя tolower также определяется в <locale>, Этот конкретный случай обсуждается в отчете об ошибках GCC. Использование оболочки, скомпилированной в отдельном модуле компиляции, который не включает конфликтующие объявления, решит эту проблему.

Другие вопросы по тегам