Sobrecargas de las Diferentes Referencias en C++

Este artículo se centra en las sobrecargas de funciones/métodos por referencias, así como en los tipos de argumentos que se pueden pasar.

requisitos previos:

Descripción general:
el valor l se refiere a una ubicación de memoria que identifica un objeto. El valor r se refiere al valor de datos que se almacena en alguna dirección en la memoria. Las referencias en C++ no son más que la alternativa a la variable ya existente. Se declaran usando el ‘&’ antes del nombre de la variable. Las referencias de rvalue se han agregado desde el surgimiento de Modern C++ (es decir, desde C++11 ).
Como resultado, ahora hay tres opciones de Call-By-Reference:

  1. Por medio de punteros.
  2. Mediante el uso de referencias lvalue (ordinarias).
  3. Mediante el uso de referencias rvalue (que se introdujeron por primera vez en C++ 11).

Aquí solo se mostrarían todas las sobrecargas posibles de las referencias lvalue y rvalue.

Nota: 
Por conveniencia, std::string se usa como argumento en los siguientes ejemplos. También se puede utilizar cualquier otro tipo de argumento (incluido el tipo definido por el usuario).

Sobrecargas de función tomando referencia de valor l y referencia de valor r-

  1. Referencia de valor L no constante.
  2. Referencia de valor constante.
  3. Referencia de valor no constante.
  4. Referencia de valor constante.

Referencia de valor L no constante

En esto, una función acepta una referencia de lvalue no constante como argumento, esto significa que uno puede modificar el parámetro proporcionado.

Sintaxis:

void foo(std::string& str); // sobrecarga de referencia de lvalue no constante

  • La función foo() acepta una referencia de lvalue no constante como argumento, lo que implica que uno puede modificar (leer/escribir) el parámetro proporcionado.
  • Tipo de variables/objetos que se pueden pasar con respecto a la firma de la función (consulte el programa a continuación)-
    1. Solo un objeto modificable con nombre. (Caso 1 en el siguiente programa).

A continuación se muestra el programa C++ para implementar el enfoque anterior:

C++14

// C++ program to implement
// the above approach
  
// for std::cout, std::endl
#include <iostream>
  
// for std::string
#include <string>
  
// for EXIT_SUCCESS
#include <cstdlib>
  
// for std::move()
#include <utility>
  
// Declaration
  
// A foo() function takes the
// argument of non-const lvalue
// reference
void foo(std::string& str);
  
// Driver code
int main()
{
    // Case 1 - A named non-const object
    // non-const object
    std::string namedNonConstObj{
        "This is named non-const object"
    };
    foo(namedNonConstObj);
  
    // Case 2 - A named const object
    // const object
    const std::string namedConstObject{
        "This is named const object"
    };
  
    // Error
    // foo(namedConstObject);
  
    // Case 3 - A unnamed temporary object
    // Error
    // foo(std::string("This is unnamed
    // temporary object"));
  
    // Case 4 - using std::move() for named
    // non-const object
    std::string namedNonConstObjWithMove{
        "This is named non-const object - using std::move()"
    };
  
    // Error
    // foo(std::move(namedNonConstObjWithMove));
  
    // Case 5 - using std::move() for named const object
    const std::string namedConstObjectWithMove{
        "This is named const object - using std::move()"
    };
  
    // foo(std::move(namedConstObjectWithMove));
    // Error
  
    /* Case 6 - using std::move() for unnamed 
  // temporary object */
    // foo(std::move(std::string("This is
    // unnamed temporary object - using
    // std::move()")));
    // Error
    return EXIT_SUCCESS;
}
  
// Definition
void foo(std::string& str)
{
    // do something
    static int counter{ 1 };
    std::cout << counter++ << ". " << str << std::endl;
    // do something
}
Producción

1. Esto se llama objeto no constante

Explicación:
Caso 1: las referencias a objetos no constantes pueden apuntar a objetos no constantes .
Caso 2: las referencias a objetos que no son constantes no pueden apuntar a objetos constantes .
Caso 3: intentará utilizar la semántica de movimiento implícitamente, lo que cambia la referencia de lvalue a una referencia de rvalue , a pesar de que no hay ninguna función que acepte una referencia de rvalue como entrada. Como resultado, la semántica de copia se utilizará como respaldo de la semántica de movimiento. Pero para la semántica de copia, se requiere una función que acepte una referencia const lvalue como argumento, que está ausente en este caso.
Caso 4 a 6:  Los casos 4, 5 y 6 son idénticos al caso 3, con la excepción de que explícitamente especificamos llamar a una función/método que acepta una referencia de valor r como argumento al marcar los objetos con std::move(). 

Referencia de valor constante

En esto, una función acepta un argumento const lvalue, lo que significa que no es posible ninguna modificación, solo se puede leer el argumento proporcionado.

void foo(const std::string& str); // sobrecarga de referencia de valor constante

  • Esta función foo() toma un argumento de referencia const lvalue, lo que implica que uno solo puede leer el argumento proporcionado.
  • Tipo de variables/objetos que se pueden pasar con respecto a la firma de la función (Consulte el programa a continuación) –
    1. Un objeto con nombre modificable . (Caso 1 en el siguiente programa).
    2. Un objeto con nombre constante. (Caso 2 en el siguiente programa).
    3. Un objeto temporal (sin nombre). (Caso 3 en el siguiente programa).
    4. Un objeto marcado con std::move(). (Caso 4 a 6 en el siguiente programa).

Nota: 
No es necesario marcar un objeto temporal con std::move() cuando una función o método está sobrecargado con un argumento de referencia rvalue. 

A continuación se muestra el programa C++ para implementar el enfoque anterior:

C++14

// C++ program to implement
// the above approach
  
// for std::cout, std::endl
#include <iostream>
  
// for std::string
#include <string>
  
// for EXIT_SUCCESS
#include <cstdlib>
  
// for std::move()
#include <utility>
  
// Declaration
  
// A foo() function takes the
// argument of const lvalue reference
void foo(const std::string& str);
  
// Driver code
int main()
{
    // Case 1 - A named non-const object
    std::string namedNonConstObj{
        "This is named non-const object"
    };
  
    // namedNonConstObj will be treated
    // as constant
    foo(namedNonConstObj);
  
    // Case 2 - A named const object
    const std::string namedConstObject{
        "This is named const object"
    };
    foo(namedConstObject);
  
    // Case 3 - A unnamed temporary
    // object
    foo(std::string(
        "This is unnamed temporary object"));
  
    // Case 4 - using std::move() for
    // named non-const object
    std::string namedNonConstObjWithMove{
        "This is named non-const object - using std::move()"
    };
    foo(std::move(namedNonConstObjWithMove));
  
    // Case 5 - using std::move() for
    // named const object
    const std::string namedConstObjWithMove{
        "This is named const object - using std::move()"
    };
    foo(std::move(namedConstObjWithMove));
  
    // Case 6 - using std::move() for
    // unnamed temporary object
    foo(std::move(std::string(
        "This is unnamed temporary object - using std::move()")));
  
    return EXIT_SUCCESS;
}
  
// Definition
void foo(const std::string& str)
{
    // do something
    static int counter{ 1 };
    std::cout << counter++ << ". " << str << std::endl;
    // do something
}
Producción

1. Este se denomina objeto no constante
2. Este se denomina objeto constante
3. Este es un objeto temporal sin nombre
4. Este se denomina objeto no constante, utilizando std::move()
5. Este se denomina objeto constante, utilizando std ::move()
6. Este es un objeto temporal sin nombre – usando std::move()

Explicación:
Caso 1:  las referencias a objetos constantes pueden apuntar a objetos que no son constantes.
Caso 2 : las referencias a objetos constantes pueden apuntar a objetos constantes.
Caso 3:  intentará utilizar la semántica de movimiento de forma implícita, lo que cambia la referencia de lvalue a una referencia de rvalue, a pesar de que no hay ninguna función que acepte una referencia de rvalue como entrada. Como resultado, la semántica de copia se utilizará como respaldo de la semántica de movimiento. Y para la semántica de copia, se requiere una función que acepte una referencia const lvalue como argumento, que está presente en este caso. Esta es la razón de la compilación exitosa de la llamada de función. 
Caso 4 a 6:  Los casos 4, 5 y 6 son idénticos al caso 3, con la excepción de que especificamos explícitamente llamar a una función/método que acepta una referencia de valor r como argumento al marcar los objetos con std::move(). 

Referencia de valor no constante

En esto, una función acepta una referencia de valor no constante, lo que significa que uno puede modificar el parámetro de pases.

void foo(std::string && str); // sobrecarga de referencia de valor no constante

  • Esta función foo() acepta una referencia de valor no constante como parámetro de entrada, lo que implica que puede modificar (leer/escribir) el parámetro pasado. Sin embargo, las referencias de rvalue se utilizan asociadas con la semántica de movimiento para robar recursos.
  • Tipo de variables/objetos que se pueden pasar con respecto a la firma de la función (consulte el programa a continuación)-
    1. Un objeto temporal que no tiene nombre, es decir, un objeto sin nombre. (Caso 3 en el siguiente programa).
    2. Un objeto no constante marcado con std::move(). (Caso 4 en el siguiente programa).

A continuación se muestra el programa C++ para implementar el enfoque anterior:

C++14

// C++ program to implement
// the above approach
  
// for std::cout, std::endl
#include <iostream>
  
// for std::string
#include <string>
  
// for EXIT_SUCCESS
#include <cstdlib>
  
// for std::move()
#include <utility>
  
// Declaration
  
// A foo() function takes the argument
// of non-const rvalue reference
void foo(std::string&& str);
  
// Driver code
int main()
{
    // Case 1 - A named non-const object
    std::string namedNonConstObj{
        "This is named non-const object"
    };
  
    // foo(namedNonConstObj);
    // Error
  
    // Case 2 - A named const object
    const std::string namedConstObject{
        "This is named const object"
    };
  
    // foo(namedConstObject);
    // Error
  
    // Case 3 - A unnamed temporary object
    foo(std::string(
        "This is unnamed temporary object"));
  
    // Case 4 - using std::move() for
    // named non-const object
    std::string namedNonConstObjWithMove{
        "This is named non-const object - using std::move()"
    };
    foo(std::move(namedNonConstObjWithMove));
  
    // Case 5 - using std::move() for
    // named const object
    const std::string namedConstObjWithMove{
        "This is named const object - using std::move()"
    };
  
    // foo(std::move(namedConstObjWithMove));
    // Error
  
    // Case 6 - using std::move() for
    // unnamed temporary object
    // Use of std::move() with temporary
    // objects is not recommended,
    // if the function with rvalue reference
    // as an argument exist.
    foo(std::move(
        std::string(
            "This is unnamed temporary object - using std::move()")));
  
    return EXIT_SUCCESS;
}
  
// Definition
void foo(std::string&& str)
{
    // do something
    static int counter{ 1 };
    std::cout << counter++ << ". " << str << std::endl;
    // do something
}
Producción

1. Este es un objeto temporal sin nombre
2. Este es un objeto no constante con nombre – usando std::move()
3. Este es un objeto temporal sin nombre – usando std::move()

Explicación:
Caso 1:  los objetos lvalue no constantes no se pueden pasar a una función que toma referencias rvalue no constantes como argumento (a menos que un objeto esté marcado con std::move()).
Caso 2 y 5:  los objetos const lvalue no se pueden pasar a una función que toma referencias no const rvalue como argumento (incluso después de que un objeto se marca con std::move()).
Caso 3:  El compilador indicará que se debe usar la función que toma como argumento una referencia rvalue. En nuestro ejemplo, existe. Caso 4:  los objetos lvalue no constantes se pueden pasar a una función que toma referencias rvalue no constantes como argumento solo si un objeto está marcado con std::move().
Caso 6:  Similar al caso 3. Si existe una función que toma una referencia rvalue como argumento, no se recomienda usar std::move() con objetos temporales. 

Referencia de valor constante:
en esto, una función toma una referencia de valor constante, lo que significa que solo se puede leer el parámetro proporcionado.

void foo(const std::string && str); // sobrecarga de referencia de valor constante

  • Esta función foo() toma un argumento de referencia const rvalue , lo que implica que solo puede leer el argumento pasado.
  • Tipo de variables/objetos que se pueden pasar con respecto a la firma de la función (Consulte el programa a continuación) –
    1. Un objeto temporal que no tiene nombre. (Caso 3 en el siguiente programa).
    2. Un objeto constante o no constante marcado con std::move(). ( Caso 4 y 5 en el programa a continuación).
  • Debido a que las referencias de rvalue están destinadas a robar recursos, ser el constspecifier con una referencia de rvalue al objeto contradice. Robar los recursos de los objetos constantes tampoco tiene sentido.

A continuación se muestra el programa C++ para implementar el enfoque anterior:

C++14

// C++ program to implement
// the above approach
  
// for std::cout, std::endl
#include <iostream>
  
// for std::string
#include <string>
  
// for EXIT_SUCCESS
#include <cstdlib>
  
// for std::move()
#include <utility>
  
// Declaration
  
// A foo() function takes the
// argument of const rvalue reference
void foo(const std::string&& str);
  
// Driver code
int main()
{
    // Case 1 - A named non-const object
    std::string namedNonConstObj{
        "This is named non-const object"
    };
  
    // Error
    // foo(namedNonConstObj);
  
    // Case 2 - A named const object
    const std::string namedConstObject{
        "This is named const object"
    };
  
    // Error
    // foo(namedConstObject);
  
    // Case 3 - A unnamed temporary object
    foo(std::string(
        "This is unnamed temporary object"));
  
    // Case 4 - using std::move() for
    // named non-const object
    std::string namedNonConstObjWithMove{
        "This is named non-const object - using std::move()"
    };
    foo(std::move(namedNonConstObjWithMove));
  
    // Case 5 - using std::move() for
    // named const object
    const std::string namedConstObjWithMove{
        "This is named const object - using std::move()"
    };
    foo(std::move(namedConstObjWithMove));
  
    // Case 6 - using std::move() for
    // unnamed temporary object
    // Use of std::move() with temporary
    // objects is not recommended,
    // if the function with rvalue
    // reference as an argument exist.
    foo(std::move(std::string(
        "This is unnamed temporary object - using std::move()")));
  
    return EXIT_SUCCESS;
}
  
// Definition
void foo(const std::string&& str)
{
    // do something
    static int counter{ 1 };
    std::cout << counter++ << ". " << str << std::endl;
    // do something
}
Producción

1. Este es un objeto temporal sin nombre
2. Este es un objeto no constante con nombre – usando std::move()
3. Este es un objeto const llamado – usando std::move()
4. Este es un objeto temporal sin nombre – usando std: :Muevete()

Explicación:
Caso 1 y 2: los objetos con nombre const o no const no se pueden pasar a una función que toma referencias a const rvalue como argumento.
Caso 3: También se pueden pasar objetos temporales a la función aceptando referencias de valores constantes.
Caso 4 y 5: solo si los objetos con nombre const o no const se indican explícitamente con std::move() pueden proporcionarse a una función que toma una referencia const rvalue como parámetro.
Caso 6: No es necesario marcar un objeto temporal con std::move() cuando una función está sobrecargada con un argumento de referencia de valor constante o no constante .

Resumen:
los tipos de argumentos que se pueden pasar a la función o método sobrecargado con referencias.

  • Sin el uso de std::move() al llamar a una función/método que toma el argumento de referencia constante o no constante de valor l o referencia de valor r.

  • Con el uso de std::move() al llamar a una función/método que toma el argumento de referencia constante o no constante de lvalue y/o referencia de rvalue.

Publicación traducida automáticamente

Artículo escrito por mayurkedari16 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *