Namespaces
Variants

Address of an overloaded function

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

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

# Контекст Цель
1 инициализатор в объявлении объекта или ссылки инициализируемый объект или ссылка
2 в правой части встроенного выражения присваивания левая часть встроенного присваивания
3 как аргумент вызова функции параметр функции
4 как аргумент пользовательского оператора параметр оператора
5 оператор return возвращаемое значение функции или преобразования
6 аргумент явного приведения или static_cast соответствующее приведение
7 константный аргумент шаблона соответствующий параметр шаблона

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

Если целевой тип содержит тип-заполнитель , выполняется выведение типа-заполнителя, и следующее описание использует выведенный тип в качестве целевого типа.

(since C++26)

Содержание

Выбор функций

Когда берётся адрес перегруженной функции, из набора перегрузок, на который ссылается имя перегруженной функции, выбирается множество S функций:

  • Если нет целевого объекта, выбираются все именованные нешаблонные функции.
  • В противном случае нешаблонная функция с типом F выбирается для функционального типа FT целевого типа, если F (после возможного применения преобразования указателя на функцию ) (since C++17) идентичен FT . [1]
  • Специализация (если есть), сгенерированная с помощью вывода аргументов шаблона для каждой именованной шаблонной функции, также добавляется в S .

Если целевой объект имеет тип указателя на функцию или ссылки на функцию, S может включать только нечленные функции , явные объектные функции-члены (начиная с C++23) и статические функции-члены. Если целевой объект имеет тип указателя на функцию-член, S может включать только неявные объектные функции-члены.

  1. Другими словами, класс, членом которого является функция, игнорируется, если целевой тип является типом указателя-на-функцию-член.

Устранение функций

После формирования множества S , функции исключаются в следующем порядке:

  • Все функции с ассоциированными constraints , которые не удовлетворены, исключаются из S .
(since C++20)
  • Если более одной функции в S остаётся, все специализации шаблонов функций в S исключаются, если S также содержит нешаблонную функцию.
(since C++20)
  • Любая заданная специализация шаблона функции spec исключается, если S содержит вторую специализацию шаблона функции, чей шаблон функции является более специализированным чем шаблон функции spec .

После таких исключений (если таковые имеются), в множестве S должна остаться ровно одна выбранная функция. В противном случае программа является некорректной.

Пример

int f(int) { return 1; }
int f(double) { return 2; }
void g(int(&f1)(int), int(*f2)(double)) { f1(0); f2(0.0); }
template<int(*F)(int)>
struct Templ {};
struct Foo
{
    int mf(int) { return 3; }
    int mf(double) { return 4; }
};
struct Emp
{
    void operator<<(int (*)(double)) {}
};
int main()
{
    // 1. инициализация
    int (*pf)(double) = f; // выбирает int f(double)
    int (&rf)(int) = f; // выбирает int f(int)
    int (Foo::*mpf)(int) = &Foo::mf; // выбирает int mf(int)
    // 2. присваивание
    pf = nullptr;
    pf = &f; // выбирает int f(double)
    // 3. аргумент функции
    g(f, f); // выбирает int f(int) для первого аргумента
             // и int f(double) для второго
    // 4. пользовательский оператор
    Emp{} << f; // выбирает int f(double)
    // 5. возвращаемое значение
    auto foo = []() -> int (*)(int)
    {
        return f; // выбирает int f(int)
    };
    // 6. приведение типа
    auto p = static_cast<int(*)(int)>(f); // выбирает int f(int)
    // 7. аргумент шаблона
    Templ<f> t;  // выбирает int f(int)
    // предотвращение предупреждений "неиспользуемая переменная" как с помощью [[maybe_unused]]
    [](...){}(pf, rf, mpf, foo, p, t);
}

Отчеты о дефектах

Следующие отчеты об изменениях поведения, влияющие на дефекты, были применены задним числом к ранее опубликованным стандартам C++.

DR Applied to Behavior as published Correct behavior
CWG 202 C++98 constant template argument was not a context
of taking the address of an overloaded function
it is
CWG 250 C++98 function template specializations generated with non-deduced
template arguments were not selected from the overload set
also selected
CWG 1153 C++98 it was unclear whether a given function type matches the target type made clear
CWG 1563 C++11 it was unclear whether list-initialization is a context
of taking the address of an overloaded function
made clear

Ссылки

  • Стандарт C++23 (ISO/IEC 14882:2024):
  • 12.3 Взятие адреса перегруженной функции [over.over]
  • Стандарт C++20 (ISO/IEC 14882:2020):
  • 12.5 Взятие адреса перегруженной функции [over.over]
  • Стандарт C++17 (ISO/IEC 14882:2017):
  • 16.4 Адрес перегруженной функции [over.over]
  • Стандарт C++14 (ISO/IEC 14882:2014):
  • 13.4 Адрес перегруженной функции [over.over]
  • Стандарт C++11 (ISO/IEC 14882:2011):
  • 13.4 Взятие адреса перегруженной функции [over.over]
  • Стандарт C++98 (ISO/IEC 14882:1998):
  • 13.4 Адрес перегруженной функции [over.over]