Соглашение о связях 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. Использование оболочки, скомпилированной в отдельном модуле компиляции, который не включает конфликтующие объявления, решит эту проблему.