punteros - paso por referencia c++
Evitar que los valores no constantes se resuelvan en una referencia rvalue en lugar de una referencia const lvalue (2)
Cuando tienes una función de plantilla como esta, casi nunca quieres sobrecargarte. El parámetro T&&
es un parámetro de captura de cualquier cosa . Y puede usarlo para obtener cualquier comportamiento que desee de una sobrecarga.
#include <iostream>
#include <vector>
using namespace std;
template <class T>
void display()
{
typedef typename remove_reference<T>::type Tr;
typedef typename remove_cv<Tr>::type Trcv;
if (is_const<Tr>::value)
cout << "const ";
if (is_volatile<Tr>::value)
cout << "volatile ";
std::cout << typeid(Trcv).name();
if (is_lvalue_reference<T>::value)
std::cout << ''&'';
else if (is_rvalue_reference<T>::value)
std::cout << "&&";
std::cout << ''/n'';
}
template <class T>
void foo(T&& t)
{
display<T>();
}
int main()
{
vector<int> x;
vector<int> const cx;
foo(x); // vector<int>&
foo(vector<int>()); // vector<int>
foo(cx); // const vector<int>&
}
Estoy teniendo problemas para sobrecargar una función para tomar un valor ya sea por referencia constante o, si es un valor de r, valor de referencia. El problema es que mis valores no constantes están vinculados a la versión rvalue de la función. Estoy haciendo esto en VC2010.
#include <iostream>
#include <vector>
using namespace std;
template <class T>
void foo(const T& t)
{cout << "void foo(const T&)" << endl;}
template <class T>
void foo(T&& t)
{cout << "void foo(T&&)" << endl;}
int main()
{
vector<int> x;
foo(x); // void foo(T&&) ?????
foo(vector<int>()); // void foo(T&&)
}
La prioridad parece ser deducir foo (x) como
foo< vector<int> & >(vector<int>& && t)
en lugar de
foo< vector<int> >(const vector<int>& t)
He intentado reemplazar la versión de referencia de rvalor con
void foo(typename remove_reference<T>::type&& t)
pero esto solo tuvo el efecto de hacer que todo se resolviera a la versión de referencia const-lvalue.
¿Cómo puedo prevenir este comportamiento? Y, de todos modos, ¿por qué es este el valor predeterminado? Parece tan peligroso, dado que se permite la modificación de las referencias rvalue, esto me deja con una variable local inesperadamente modificada.
EDITAR: acaba de agregar versiones sin plantillas de las funciones, y funcionan como se esperaba. ¿Hacer la función una plantilla cambia las reglas de resolución de sobrecarga? Eso es ... realmente frustrante!
void bar(const vector<int>& t)
{cout << "void bar(const vector<int>&)" << endl;}
void bar(vector<int>&& t)
{cout << "void bar(vector<int>&&)" << endl;}
bar(x); // void bar(const vector<int>&)
bar(vector<int>()); // void bar(vector<int>&&)
Para que T&&
enlace a una referencia de valor T
, T
debe ser un tipo de referencia de valor T
. Puede prohibir que se cree una instancia de la plantilla con un tipo de referencia T
:
template <typename T>
typename std::enable_if<!std::is_reference<T>::value>::type foo(T&& t)
{
cout << "void foo(T&&)" << endl;
}
enable_if
se encuentra en <utility>
; is_reference
se encuentra en <type_traits>
.
La razón por la que la sobrecarga que toma T&&
es preferible a la sobrecarga que toma una T const&
es que T&&
es una coincidencia exacta (con T = vector<int>&
) pero T const&
requiere una conversión de calificación (se debe agregar const-qualification).
Esto solo pasa con las plantillas. Si tiene una función no estándar que toma un std::vector<int>&&
, solo podrá llamar a esa función con un argumento de valor de r. Cuando tiene una plantilla que toma un T&&
, no debe pensar en ella como "un parámetro de referencia de rvalue"; es un "parámetro de referencia universal" (creo que Scott Meyers usó un lenguaje similar). Puede aceptar cualquier cosa.
Permitir que un parámetro T&&
de una plantilla de función se vincule a cualquier categoría de argumento es lo que permite un reenvío perfecto.