Spec-Zone.ru › C++

Нестатические члены-функции

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

class S
{
    int mf1(); // non-static member function declaration
    void mf2() volatile, mf3() &&; // can have cv-qualifiers and/or a reference-qualifier
        // the declaration above is equivalent to two separate declarations:
        // void mf2() volatile;
        // void mf3() &&;
 
    int mf4() const { return data; } // can be defined inline
    virtual void mf5() final; // can be virtual, can use final/override
    S() : data(12) {} // constructors are member functions too
    int data;
};
 
int S::mf1() { return 7; } // if not defined inline, has to be defined at namespace

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

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

(с C++23)

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

Объяснение

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

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

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

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

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

struct S
{
    int n;
    void f();
};
 
void S::f()
{
    n = 1; // transformed to (*this).n = 1;
}
 
int main()
{
    S s1, s2;
    s1.f(); // changes s1.n
}

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

struct S
{
    static int n;
    void f();
};
 
void S::f()
{
    n = 1; // transformed to S::n = 1;
}
 
int main()
{
    S s1, s2;
    s1.f(); // changes 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) {}
 
    // const member function
    int operator[](int idx) const
    {                     // the this pointer has type const Array*
        return data[idx]; // transformed to (*this).data[idx];
    }
 
    // non-const member function
    int& operator[](int idx)
    {                     // the this pointer has type Array*
        return data[idx]; // transformed to (*this).data[idx]
    }
};
 
int main()
{
    Array a(10);
    a[1] = 1;  // OK: the type of a[1] is int&
    const Array ca(10);
    ca[1] = 2; // Error: the type of ca[1] is int
}

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

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

  • без квалификатора ссылок: неявный параметр объекта имеет тип lvalue ссылки на cv-квалифицированный X и дополнительно разрешает привязку аргумента rvalue неявного объекта
  • квалификатор ссылки на lvalue: неявный параметр объекта имеет тип lvalue ссылки на cv-квалифицированный X
  • квалификатор ссылки на rvalue: неявный параметр объекта имеет тип 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-квалификации, квалификация ссылками не изменяет свойства указателя this: в функции с квалификатором ссылки на rvalue, *this остаётся выражением lvalue.

(с C++11)

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

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

Явный параметр объекта

Нестатическая функция-член может быть объявлена с первым параметром — явным параметром объекта, обозначенным префиксом ключевым словом 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- и non-const функций-членов, см. оператор индексирования массива для примера.

Кроме того, явный параметр объекта выводится до производного типа, что упрощает CRTP:

// a CRTP trait
struct add_postfix_increment
{
    template<typename Self>
    auto operator++(this Self&& self, int)
    {
        auto tmp = self; // Self deduces to "some_type"
        ++self;
        return tmp;
    }
};
 
struct some_type : add_postfix_increment
{
    some_type& operator++() { ... }
};

В теле функции с явным параметром объекта указатель 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


Функции-члены с явным параметром объекта не могут быть статическими или виртуальными, и они не могут иметь cv- или ref-квалификаторы.

(с C++23)

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

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

  • Конструктор по умолчанию
  • Копирующий конструктор
  • Перемещающий конструктор
(с C++11)
  • Оператор копирования
  • Оператор перемещения
(с C++11)
  • Деструктор(до C++20)Прогнозируемый деструктор(с C++20)

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

Примечания

Макрос проверки функций Значение Std Функция
__cpp_ref_qualifiers 200710L (C++11) квалификаторы ссылок
__cpp_explicit_this_parameter 202110L (C++23) Явный параметр объекта

Пример

#include <exception>
#include <iostream>
#include <string>
#include <utility>
 
struct S
{
    int data;
 
    // simple converting constructor (declaration)
    S(int val);
 
    // simple explicit constructor (declaration)
    explicit S(std::string str);
 
    // const member function (definition)
    virtual int getData() const { return data; }
};
 
// definition of the constructor
S::S(int val) : data(val)
{
    std::cout << "ctor1 called, data = " << data << '\n';
}
 
// this constructor has a catch clause
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; // ctor's catch clause should always rethrow
}
 
struct D : S
{
    int data2;
    // constructor with a default argument
    D(int v1, int v2 = 11) : S(v1), data2(v2) {}
 
    // virtual member function
    int getData() const override { return data * data2; }
 
    // lvalue-only assignment operator
    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: assignment to lvalue
//  D(5) = d1; // ERROR: no suitable overload of operator=
}

Вывод:

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

Отчёты об ошибках

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

Отчёт об ошибке Применено к Поведение, как опубликовано Правильное поведение
CWG 194 C++98 неопределённо, может ли нестатический член-функция
иметь то же имя, что и имя окружающего класса
добавленное явное ограничение именования

См. также

  • классы
  • нестатические члены данных
  • статические члены данных

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/language/member_functions

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API