Namespaces
Variants

Non-static member functions

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

Нестатическая функция-член — это функция, объявленная в спецификации класса без использования спецификаторов static или friend (см. статические функции-члены и объявление дружественной функции для понимания эффекта этих ключевых слов).

class S
{
    int mf1(); // объявление нестатической функции-члена
    void mf2() volatile, mf3() &&; // может иметь cv-квалификаторы и/или ссылочный квалификатор
        // объявление выше эквивалентно двум отдельным объявлениям:
        // void mf2() volatile;
        // void mf3() &&;
    int mf4() const { return data; } // может быть определено внутри класса
    virtual void mf5() final; // может быть виртуальной, может использовать final/override
    S() : data(12) {} // конструкторы также являются функциями-членами
    int data;
};
int S::mf1() { return 7; } // если не определено внутри класса, должно быть определено в пространстве имен

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

Явная объектная функция-член — это нестатическая функция-член с явным объектным параметром .

(since C++23)

Неявная функция-член объекта — это нестатическая функция-член без явного параметра объекта (до C++23 это был единственный вид нестатических функций-членов, поэтому в литературе они назывались «нестатическими функциями-членами»).

Содержание

Объяснение

Любые объявления функций разрешены, с дополнительными синтаксическими элементами, доступными только для нестатических функций-членов: чистые спецификаторы , cv-квалификаторы , ref-квалификаторы, final и override спецификаторы (начиная с C++11) , и списки инициализации членов .

Нестатическая функция-член класса X может быть вызвана

1) Для объекта типа X с использованием оператора доступа к члену класса
2) Для объекта класса, производного от X
3) Непосредственно из тела функции-члена класса X
4) Непосредственно из тела функции-члена класса, производного от X

Вызов нестатической функции-члена класса X для объекта, который не является типом X или типом, производным от X , вызывает неопределённое поведение.

В теле нестатической функции-члена класса X любое id-выражение e (например, идентификатор), которое разрешается в не-типовой нестатический член X или базового класса X , преобразуется в выражение доступа к члену ( * this ) . e (если оно уже не является частью выражения доступа к члену). Это не происходит в контексте определения шаблона, поэтому имя может потребоваться явно предварить this - > , чтобы стать зависимым .

struct S
{
    int n;
    void f();
};
void S::f()
{
    n = 1; // преобразуется в (*this).n = 1;
}
int main()
{
    S s1, s2;
    s1.f(); // изменяет s1.n
}

В теле нестатической функции-члена класса X любой неквалифицированный идентификатор, который разрешается в статический член, перечислитель или вложенный тип класса X или базового класса X , преобразуется в соответствующий квалифицированный идентификатор:

struct S
{
    static int n;
    void f();
};
void S::f()
{
    n = 1; // преобразуется в S::n = 1;
}
int main()
{
    S s1, s2;
    s1.f(); // изменяет S::n
}

Функции-члены с cv-квалификаторами

Неявная функция-член объекта может быть объявлена с последовательностью cv-квалификаторов ( const , volatile или комбинацией const и volatile ), эта последовательность указывается после списка параметров в объявлении функции . Функции с разными последовательностями cv-квалификаторов (или без них) имеют разные типы и поэтому могут перегружать друг друга.

В теле функции с последовательностью cv-квалификаторов, * this является cv-квалифицированным, например, в функции-члене с квалификатором const , обычно могут вызываться только другие функции-члены с квалификатором const . Функция-член без квалификатора const всё же может быть вызвана, если применён const_cast или через путь доступа, который не затрагивает this .

#include <vector>
struct Array
{
    std::vector<int> data;
    Array(int sz) : data(sz) {}
    // константная функция-член
    int operator[](int idx) const
    {                     // указатель this имеет тип const Array*
        return data[idx]; // преобразуется в (*this).data[idx];
    }
    // неконстантная функция-член
    int& operator[](int idx)
    {                     // указатель this имеет тип Array*
        return data[idx]; // преобразуется в (*this).data[idx]
    }
};
int main()
{
    Array a(10);
    a[1] = 1;  // OK: тип a[1] - int&
    const Array ca(10);
    ca[1] = 2; // Ошибка: тип ca[1] - int
}

Функции-члены с ref-квалификатором

Неявная функция-член объекта может быть объявлена без ref-квалификатора, с lvalue ref-квалификатором (токен & после списка параметров) или с rvalue ref-квалификатором (токен && после списка параметров). Во время разрешения перегрузки , неявная функция-член объекта с последовательностью cv-квалификаторов класса X обрабатывается следующим образом:

  • без ref-квалификатора: неявный параметр объекта имеет тип lvalue-ссылка на cv-квалифицированный X и дополнительно может связываться с rvalue неявным аргументом объекта
  • lvalue ref-квалификатор: неявный параметр объекта имеет тип lvalue-ссылка на cv-квалифицированный X
  • rvalue ref-квалификатор: неявный параметр объекта имеет тип rvalue-ссылка на cv-квалифицированный X
#include <iostream>
struct S
{
    void f() &  { std::cout << "lvalue\n"; }
    void f() && { std::cout << "rvalue\n"; }
};
int main()
{
    S s;
    s.f();            // prints "lvalue"
    std::move(s).f(); // prints "rvalue"
    S().f();          // prints "rvalue"
}

Примечание: в отличие от cv-квалификации, ref-квалификация не изменяет свойства указателя this : внутри функции с rvalue ref-квалификатором, * this остается lvalue-выражением.

(начиная с C++11)

Виртуальные и чисто виртуальные функции

Нестатическая функция-член может быть объявлена виртуальной или чисто виртуальной . Подробности смотрите в разделах виртуальные функции и абстрактные классы .

Явные объектные функции-члены

Для нестатической невиртуальной функции-члена, не объявленной с cv-квалификатором или ref-квалификатором, её первый параметр, если он не является пакетом параметров функции , может быть явным объектным параметром (обозначается с помощью префиксного ключевого слова this ):

struct X
{
    void foo(this X const& self, int i); // same as void foo(int i) const &;
//  void foo(int i) const &; // Error: already declared
    void bar(this X self, int i); // pass object by value: makes a copy of “*this”
};

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

struct X
{
    template<typename Self>
    void foo(this Self&&, int);
};
struct D : X {};
void ex(X& x, D& d)
{
    x.foo(1);       // Self = X&
    move(x).foo(2); // Self = X
    d.foo(3);       // Self = D&
}

Это позволяет устранить дублирование const- и не-const функций-членов, см. оператор индексации массива для примера.

В теле явной объектной функции-члена указатель this не может использоваться: все обращения к членам должны выполняться через первый параметр, как в статических функциях-членах:

struct C
{
    void bar();
    void foo(this C c)
    {
        auto x = this; // error: no this
        bar();         // error: no implicit this->
        c.bar();       // ok
    }
};

Указатель на явную объектную функцию-член является обычным указателем на функцию, а не указателем на член:

struct Y 
{
    int f(int, int) const&;
    int g(this Y const&, int, int);
};
auto pf = &Y::f;
pf(y, 1, 2);              // error: pointers to member functions are not callable
(y.*pf)(1, 2);            // ok
std::invoke(pf, y, 1, 2); // ok
auto pg = &Y::g;
pg(y, 3, 4);              // ok
(y.*pg)(3, 4);            // error: “pg” is not a pointer to member function
std::invoke(pg, y, 3, 4); // ok
(начиная с C++23)

Специальные функции-члены

Некоторые функции-члены являются специальными : при определённых обстоятельствах они определяются компилятором, даже если не определены пользователем. К ним относятся:

(начиная с C++11)
(начиная с C++11)

Специальные функции-члены вместе с операторами сравнения (начиная с C++20) являются единственными функциями, которые могут быть дефолтными , то есть определены с использованием = default вместо тела функции (подробности см. на соответствующих страницах).

Примечания

Макрос тестирования возможностей Значение Стандарт Возможность
__cpp_ref_qualifiers 200710L (C++11) ref-qualifiers
__cpp_explicit_this_parameter 202110L (C++23) explicit object parameter ( deducing this )

Пример

#include <exception>
#include <iostream>
#include <string>
#include <utility>
struct S
{
    int data;
    // простой преобразующий конструктор (объявление)
    S(int val);
    // простой явный конструктор (объявление)
    explicit S(std::string str);
    // константная функция-член (определение)
    virtual int getData() const { return data; }
};
// определение конструктора
S::S(int val) : data(val)
{
    std::cout << "ctor1 called, data = " << data << '\n';
}
// этот конструктор имеет блок catch
S::S(std::string str) try : data(std::stoi(str))
{
    std::cout << "ctor2 called, data = " << data << '\n';
}
catch(const std::exception&)
{
    std::cout << "ctor2 failed, string was '" << str << "'\n";
    throw; // блок catch конструктора всегда должен повторно выбрасывать исключение
}
struct D : S
{
    int data2;
    // конструктор с аргументом по умолчанию
    D(int v1, int v2 = 11) : S(v1), data2(v2) {}
    // виртуальная функция-член
    int getData() const override { return data * data2; }
    // оператор присваивания только для lvalue
    D& operator=(D other) &
    {
        std::swap(other.data, data);
        std::swap(other.data2, data2);
        return *this;
    }
};
int main()
{
    D d1 = 1;
    S s2("2");
    try
    {
        S s3("not a number");
    }
    catch(const std::exception&) {}
    std::cout << s2.getData() << '\n';
    D d2(3, 4);
    d2 = d1;   // OK: присваивание lvalue
//  D(5) = d1; // ОШИБКА: нет подходящей перегрузки operator=
}

Вывод:

ctor1 called, data = 1
ctor2 called, data = 2
ctor2 failed, string was 'not a number'
2
ctor1 called, data = 3

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

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

DR Applied to Behavior as published Correct behavior
CWG 194 C++98 неясно, может ли нестатическая функция-член
иметь то же имя, что и имя объемлющего класса
добавлено явное ограничение на именование

Смотрите также