Иногда людям приходится работать с указателями на функции. И у них может возникнуть надобность взять адрес у стдшной функции. Например, вы хотите как-то трансформировать каждый символ в строке c помощью std::transform и в качестве коллбэка передаете именно указатель на функцию
std::transform(s.begin(), s.end(), s.begin(), std::toupper);
Вроде ничего страшного не должно случиться. Но согласно стандарту, поведение программы в этом случае unspecified и потенциально даже ill-formed.
Все потому что нельзя брать адреса у стандартных функций. Начиная с С++20 это явно прописано в стандарте.
Let F denote a standard library function, a standard
library static member function, or an instantiation of
a standard library function template.
Unless F is designated an addressable function,
the behavior of a C++ program is unspecified
(possibly ill-formed) if it explicitly or implicitly
attempts to form a pointer to F.
Possible means of forming such pointers include
application of the unary & operator, addressof,
or a function-to-pointer standard conversion.
Moreover, the behavior of a C++ program is
unspecified (possibly ill-formed) if it attempts
to form a reference to F or if it attempts to form
a pointer-to-member designating either a standard library
non-static member function or an instantiation of
a standard library member function template.
Нельзя формировать указатели и ссылки на стандартные функции и нестатические методы.
Но почему? Чем они отличаются от обычных функций?
На самом деле ничем. Дело в том, что будет с функциями в будущем.
Вы без проблем получите адрес одиночной функции. Но если вы добавите к ней перегрузку, то у вас все резко перестанет компилироваться, потому что компилятор не сможет понять, какую конкретно вы используете перегрузку.
А стандарт - это штука не статичная. В него постоянно добавляются новые фичи и обновляются в том числе старые инструменты. Например, с приходом С++11 у нас появилась мув-семантика. И условный метод вектора push_back обзавелся новой перегрузкой для правой ссылки. И это сломало код, который брал адрес метода push_back.
#include <vector>
template<typename T>
void Invoke(std::vector<int>& vec, T mem_fun_ptr, int arg)
{
(vec.*mem_fun_ptr)(arg);
}
int main()
{
std::vector<int> vec;
Invoke(vec, &std::vector<int>::push_back, 42);
}
Этот код успешно собирается на 98 плюсах, но не может этого сделать на 11-м стандарте. Можете поиграться с примером на годболте.
Если вам все равно нужно сделать что-то подобное, то оберните вызов стандартной функции в лямбду. Тогда вы ничего не нарушите.
std::transform(s.begin(),
s.end(),
s.begin(),
[](unsigned char c){ return std::toupper(c); });
Don't break your future. Stay cool.
#cppcore #cpp20