mapa - c++{* this} dentro de llaves
string map c++ (4)
¡Este es bueno! Esto se debe a que return {...}
significa "devolver un objeto del tipo de retorno de la función inicializado con el inicializador de lista ...".
Los inicializadores de lista se describen con más detalle aquí:
http://en.cppreference.com/w/cpp/language/list%20initialization
Entonces, la diferencia es que {*this}
llama a esto:
inline Position(const Obj& ref): ref(ref){}
Mientras que *this
intenta convertir Obj&
to Position
mediante el uso de operadores de asignación eliminados explícitamente (antes de C ++ 11, tendrían que ser private
, y obtendría un mensaje de error aún más confuso si los inicializadores de lista estuvieran disponibles ...) :
inline Position(Position const &) = delete;
inline Position(Position &&) = delete;
El siguiente código compila bien:
g++ -std=c++11 test.cpp -Wall -Wextra -Wfatal-errors && ./a.out
Sin embargo, si quito las llaves de {*this}
y uso *this
en *this
lugar, me enfrentaré con un error:
error: uso de la función eliminada ''Obj :: Position :: Position (Obj :: Position &&)''
¿Cuál es la diferencia entre {*this}
y *this
?
class Obj
{
template<bool> friend class Position;
double data;
public:
class Position
{
const Obj& ref;
public:
inline Position(const Obj& ref): ref(ref){}
inline Position(Position const &) = delete;
inline Position(Position &&) = delete;
};
inline Obj(){}
inline Obj(const double &data): data(data){}
inline auto get_pos() const-> Position{return {*this};} /* <--- here */
inline auto get_pos()-> Position{return {*this};}
};
int main()
{
return 0;
}
Cuando las llaves están presentes, está copy-list-initializing el valor de retorno, no está involucrado ningún constructor de copiar / mover. El valor de retorno se construye in situ utilizando el Position(const Obj&)
.
Tenga en cuenta que el código no se compilará incluso con las llaves si hace que el Position(const Obj&)
explicit
porque la inicialización de la lista de copia no permite que se llame a los constructores explícitos.
Si omite las llaves, entonces semánticamente se construye un objeto de Position
temporal dentro de la función, y el valor de retorno se construye a partir de ese temporal. En la práctica, la mayoría de las implementaciones eludirán la construcción del movimiento, pero aún requiere que esté presente un constructor de movimiento viable, lo que no es el caso aquí porque se ha eliminado explícitamente. Esta es la razón por la que su código no se compilará sin llaves.
Usando un compilador de C ++ 17, su código se compilará incluso sin las llaves debido a la elección de copia garantizada .
Francamente, usando tu clase y el siguiente main ():
int main()
{
Obj o1;
cout<<"get position"<<''/n'';
Obj::Position pos= o1.get_pos();
cout.flush();
return 0;
}
no compila (gcc / mingw) en ambos casos (-std = c ++ 14), con o sin llaves y se queja de la falta del constructor de la Posición (Posición &&), que se elimina. Es razonable porque parece que en ambos casos se realiza una construcción del objeto de retorno temporal, que luego se moverá al destino. Lo cual es imposible ya que se borra el constructor de movimiento. A la inversa, utilizando el indicador -std = c ++ 17 que compila en ambos casos (con o sin llaves), lo más probable es que estemos alcanzando la optimización del valor de retorno garantizado de c ++ 17. Espero que esto ayude.
La diferencia entre los dos es realmente muy sutil. C ++ 11 introdujo la inicialización de la lista de características (también llamada a veces inicialización de llaves ):
Antes de C ++ 11, cuando desea crear de forma predeterminada y un objeto o
de tipo Obj
y construir una Position p
desde o
, debe escribir
Obj o; // default construct o
Obj::Position p(o); // construct p using Position(Obj const&)
Un error común para los principiantes (especialmente con un fondo Java) fue intentar escribir esto:
Obj o(); // mistake: declares a function o returning an Obj
Obj::Position p(o); // error: no constructor takes a function
La primera línea declara una function , y la segunda intenta crear una Position
utilizando un constructor que toma un puntero de función como su argumento. Para tener una sintaxis de inicialización uniforme, C ++ 11 introdujo la inicialización de la lista:
Obj oo{}; // new in C++11: default construct o of type Obj
Obj::Position p1(oo); // possible before (and after) C++11
Obj::Position p2{oo}; // new in C++11: construct p2 using Position(Obj const&)
Esta nueva sintaxis también funciona en return
retorno, y esto conduce a la respuesta de su pregunta: la diferencia entre return {*this};
y return *this;
es que el primero inicializa el valor de retorno directamente desde *this
, mientras que el último primero convierte *this
en un objeto de Position
temporal y luego inicializa el valor de retorno indirectamente desde este temporal , que falla porque el constructor de copia y movimiento han sido explícitamente eliminado
Como han señalado los carteles anteriores, la mayoría de los compiladores eligen estos objetos temporales porque no son realmente útiles para nada; pero esto solo es posible si se podrían usar en teoría porque está disponible una copia o un constructor de movimiento. Debido a que esto genera mucha confusión (¿por qué necesito refuerzos alrededor de mi declaración de devolución? ¿El compilador va a ocultar la copia o no?), C ++ 17 elimina estos temporales innecesarios e inicializa el valor de devolución directamente ambos casos ( return {*this};
y return *this
).
Puede intentarlo utilizando un compilador que admita C ++ 17. En el --std=c++1z
4.0 o gcc 7.1, puedes pasar --std=c++1z
, y tu código debe compilarse bien con y sin llaves.