Краткий ответ
номер
Объяснение
[namespace.std] говорит:
Пусть F
обозначает стандартную библиотечную функцию ( [global.functions] ), статическую функцию-член стандартной библиотеки или создание шаблона стандартной библиотечной функции.
Если F
не обозначено как адресуемая функция , поведение программы на C ++ не определено (возможно, плохо сформировано), если оно явно или неявно пытается сформировать указатель на F
.
[ Примечание: Возможные способы формирования таких указателей включают применение унарного оператора &
( [expr.unary.op] ), addressof
( [special. addressof] ) или стандартное преобразование функции в указатель ( [conv.func] ).
& Mdash; Конечная нота ]
Более того, поведение программы на C ++ не определено (возможно, плохо сформировано), если она пытается сформировать ссылку на F
или если она пытается сформировать указатель на член, обозначающий либо стандартную библиотеку нестатическая функция-член ( [member.functions] ) или создание шаблона функции-члена стандартной библиотеки.
Имея это в виду, давайте проверим два вызова std::invoke
.
Первый звонок
std::invoke(std::boolalpha, std::cout);
Здесь мы пытаемся сформировать указатель на std::boolalpha
. К счастью, [fmtflags.manip] спасает день:
Каждая функция, указанная в этом подпункте, является назначенной адресуемой функцией ( [namespace.std] ).
И boolalpha
- это функция, указанная в этом подпункте.
Таким образом, эта линия правильно сформирована и эквивалентна:
std::cout.setf(std::ios_base::boolalpha);
Но почему это? Ну, это необходимо для следующего кода:
std::cout << std::boolalpha;
Второй звонок
std::cout << std::invoke(static_cast<ctype_func>(std::tolower), 'A') << "\n";
К сожалению, [cctype.syn] говорит:
Содержимое и значение заголовка <cctype>
совпадают с заголовком стандартной библиотеки C <ctype.h>
.
Нигде tolower
явно не обозначена адресуемой функцией.
Следовательно, поведение этой программы на C ++ не определено (возможно, неправильно), поскольку она пытается сформировать указатель на tolower
, который не обозначен как адресуемая функция.
Заключение
Ожидаемый результат не гарантируется.
На самом деле, код даже не гарантированно компилируется.
Это также относится к функциям-членам.
[namespace.std] прямо не упоминает об этом, но из [member.functions] видно, что поведение программы на C ++ не определено (возможно, плохо сформировано), если оно пытается получить адрес объявленной функции-члена в стандартной библиотеке C ++. За [member.functions] / 2 :
Для невиртуальной функции-члена, описанной в стандартной библиотеке C ++, реализация может объявить другой набор сигнатур функций-членов при условии, что любой вызов функции-члена, который выберет перегрузку из набора объявлений, описанного в этом Документ ведет себя так, как будто эта перегрузка была выбрана. [ Примечание: Например, реализация может добавить параметры со значениями по умолчанию или заменить функцию-член аргументами по умолчанию двумя или более функциями-членами с эквивалентным поведением, или добавить дополнительные подписи для имени функции-члена. & Mdash; Конечная нота ]
И [expr.unary.op] / 6 :
Адрес перегруженной функции может быть взят только в контексте, который однозначно определяет, на какую версию перегруженной функции ссылаются (см. [Over.over]). [ Примечание: Поскольку контекст может определять, является ли операнд статической или нестатической функцией-членом, контекст также может влиять на то, имеет ли выражение тип «указатель на функцию» или «указатель на функцию-член». & Mdash; Конечная нота ]
Поэтому бехavior программы не определен (возможно, плохо сформирован), если он явно или неявно пытается сформировать указатель на функцию-член в библиотеке C ++.
(Спасибо за комментарий за указание на это!)