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

Una función miembro no estática es una función que se declara en una especificación de miembro de una clase sin un especificador static o friend (ver funciones miembro estáticas y declaración friend para el efecto de esas palabras clave).

class S
{
    int mf1(); // declaración de función miembro no estática
    void mf2() volatile, mf3() &&; // puede tener calificadores cv y/o un calificador de referencia
        // la declaración anterior es equivalente a dos declaraciones separadas:
        // void mf2() volatile;
        // void mf3() &&;
    int mf4() const { return data; } // puede definirse en línea
    virtual void mf5() final; // puede ser virtual, puede usar final/override
    S() : data(12) {} // los constructores también son funciones miembro
    int data;
};
int S::mf1() { return 7; } // si no se define en línea, debe definirse en el espacio de nombres

Constructores , destructores , y funciones de conversión utilizan sintaxis especiales para sus declaraciones. Las reglas descritas en esta página pueden no aplicarse a estas funciones. Consulte sus respectivas páginas para más detalles.

Una función miembro de objeto explícito es una función miembro no estática con un parámetro de objeto explícito .

(since C++23)

Una función miembro de objeto implícita es una función miembro no estática sin un parámetro de objeto explícito (antes de C++23, este era el único tipo de función miembro no estática, y por lo tanto se refería como "función miembro no estática" en la literatura).

Contenidos

Explicación

Se permiten declaraciones de funciones , con elementos de sintaxis adicionales que solo están disponibles para funciones miembro no estáticas: especificadores puros , calificadores cv , calificadores de referencia, final y override especificadores (desde C++11) , y listas de inicialización de miembros .

Una función miembro no estática de la clase X puede ser llamada

1) Para un objeto de tipo X utilizando el operador de acceso a miembro de clase
2) Para un objeto de una clase derivada de X
3) Directamente desde dentro del cuerpo de una función miembro de X
4) Directamente desde el cuerpo de una función miembro de una clase derivada de X

Llamar a una función miembro no estática de la clase X sobre un objeto que no es de tipo X , o de un tipo derivado de X provoca comportamiento indefinido.

Dentro del cuerpo de una función miembro no estática de X , cualquier expresión de identificador e (por ejemplo, un identificador) que se resuelva en un miembro no estático de tipo no-clase de X o de una clase base de X , se transforma en una expresión de acceso a miembro ( * this ) . e (a menos que ya forme parte de una expresión de acceso a miembro). Esto no ocurre en contexto de definición de plantilla, por lo que un nombre puede necesitar ser prefijado con this - > explícitamente para volverse dependiente .

struct S
{
    int n;
    void f();
};
void S::f()
{
    n = 1; // transformado a (*this).n = 1;
}
int main()
{
    S s1, s2;
    s1.f(); // modifica s1.n
}

Dentro del cuerpo de una función miembro no estática de X , cualquier identificador no calificado que se resuelva en un miembro estático, un enumerador o un tipo anidado de X o de una clase base de X , se transforma en el identificador calificado correspondiente:

struct S
{
    static int n;
    void f();
};
void S::f()
{
    n = 1; // transformado a S::n = 1;
}
int main()
{
    S s1, s2;
    s1.f(); // cambia S::n
}

Funciones miembro con calificadores cv

Una función miembro de objeto implícita puede declararse con una secuencia de calificadores cv ( const , volatile , o una combinación de const y volatile ), esta secuencia aparece después de la lista de parámetros en la declaración de función . Las funciones con diferentes secuencias de calificadores cv (o sin secuencia) tienen tipos diferentes y por lo tanto pueden sobrecargarse entre sí.

En el cuerpo de una función con una secuencia de calificadores cv, * this está calificado cv, por ejemplo, en una función miembro con calificador const , solo se pueden llamar normalmente otras funciones miembro con calificador const . Una función miembro sin calificador const aún puede ser llamada si se aplica const_cast o a través de una ruta de acceso que no involucre this .

#include <vector>
struct Array
{
    std::vector<int> data;
    Array(int sz) : data(sz) {}
    // función miembro const
    int operator[](int idx) const
    {                     // el puntero this tiene tipo const Array*
        return data[idx]; // transformado a (*this).data[idx];
    }
    // función miembro no const
    int& operator[](int idx)
    {                     // el puntero this tiene tipo Array*
        return data[idx]; // transformado a (*this).data[idx]
    }
};
int main()
{
    Array a(10);
    a[1] = 1;  // OK: el tipo de a[1] es int&
    const Array ca(10);
    ca[1] = 2; // Error: el tipo de ca[1] es int
}

Funciones miembro con calificador de referencia

Una función miembro de objeto implícito puede declararse sin calificador de referencia, con calificador de referencia de lvalue (el token & después de la lista de parámetros) o con calificador de referencia de rvalue (el token && después de la lista de parámetros). Durante la resolución de sobrecarga , una función miembro de objeto implícito con una secuencia de calificadores cv de clase X se trata de la siguiente manera:

  • sin calificador de referencia: el parámetro de objeto implícito tiene tipo referencia a lvalue a X calificado cv y además se permite que enlace un argumento de objeto implícito rvalue
  • calificador de referencia lvalue: el parámetro de objeto implícito tiene tipo referencia a lvalue a X calificado cv
  • calificador de referencia rvalue: el parámetro de objeto implícito tiene tipo referencia a rvalue a X calificado cv
#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"
}

Nota: a diferencia de la calificación cv, la calificación de referencia no cambia las propiedades del puntero this : dentro de una función con calificador de referencia rvalue, * this permanece como una expresión lvalue.

(desde C++11)

Funciones virtuales y puras virtuales

Una función miembro no estática puede declararse virtual o pura virtual . Consulte funciones virtuales y clases abstractas para más detalles.

Funciones miembro de objeto explícito

Para una función miembro no estática no virtual no declarada con calificador cv o calificador de referencia, su primer parámetro, si no es un paquete de parámetros de función , puede ser un parámetro de objeto explícito (denotado con la palabra clave prefijada 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”
};

Para plantillas de funciones miembro, el parámetro de objeto explícito permite la deducción del tipo y categoría de valor, esta característica del lenguaje se denomina "deducción de 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&
}

Esto hace posible eliminar la duplicación de funciones miembro const y no const, consulte operador de subíndice de array para ver un ejemplo.

Dentro del cuerpo de una función miembro de objeto explícito, el puntero this no puede utilizarse: todo acceso a miembros debe realizarse a través del primer parámetro, como en las funciones miembro estáticas:

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

Un puntero a una función miembro de objeto explícito es un puntero ordinario a función, no un puntero a miembro:

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
(desde C++23)

Funciones miembro especiales

Algunas funciones miembro son especiales : bajo ciertas circunstancias son definidas por el compilador incluso si no son definidas por el usuario. Son:

(desde C++11)
(desde C++11)

Funciones miembro especiales junto con los operadores de comparación (desde C++20) son las únicas funciones que pueden ser defaulted , es decir, definidas usando = default en lugar del cuerpo de la función (consulte sus páginas para más detalles).

Notas

Macro de prueba de características Valor Estándar Característica
__cpp_ref_qualifiers 200710L (C++11) calificadores de referencia
__cpp_explicit_this_parameter 202110L (C++23) parámetro de objeto explícito ( deducción de this )

Ejemplo

#include <exception>
#include <iostream>
#include <string>
#include <utility>
struct S
{
    int data;
    // constructor de conversión simple (declaración)
    S(int val);
    // constructor explícito simple (declaración)
    explicit S(std::string str);
    // función miembro constante (definición)
    virtual int getData() const { return data; }
};
// definición del constructor
S::S(int val) : data(val)
{
    std::cout << "ctor1 llamado, data = " << data << '\n';
}
// este constructor tiene una cláusula catch
S::S(std::string str) try : data(std::stoi(str))
{
    std::cout << "ctor2 llamado, data = " << data << '\n';
}
catch(const std::exception&)
{
    std::cout << "ctor2 falló, string era '" << str << "'\n";
    throw; // la cláusula catch del constructor siempre debe relanzar
}
struct D : S
{
    int data2;
    // constructor con un argumento por defecto
    D(int v1, int v2 = 11) : S(v1), data2(v2) {}
    // función miembro virtual
    int getData() const override { return data * data2; }
    // operador de asignación solo para lvalues
    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: asignación a lvalue
//  D(5) = d1; // ERROR: no hay sobrecarga adecuada de operator=
}

Salida:

ctor1 llamado, data = 1
ctor2 llamado, data = 2
ctor2 falló, string era 'not a number'
2
ctor1 llamado, data = 3

Informes de defectos

Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares de C++ publicados anteriormente.

DR Aplicado a Comportamiento publicado Comportamiento correcto
CWG 194 C++98 ambiguo si una función miembro no estática
podría tener el mismo nombre que el nombre de la clase contenedora
se añadió restricción de nomenclatura explícita

Véase también