c++ - ¿Por qué no funciona el reenvío de referencia en este caso?
c++11 templates (1)
#include <vector>
using namespace std;
template<typename T, typename = decltype(&T::size)>
void f1(T)
{}
template<typename T, typename = decltype(&T::size)>
void f2(T&)
{}
template<typename T, typename = decltype(&T::size)>
void f3(T&&)
{}
int main()
{
vector<int> coll;
f1(coll); // ok
f2(coll); // ok
f3(coll); // error : no matching function for call to ''f3''
}
main.cpp (21,6): nota: plantilla candidata ignorada: falla de sustitución [con
T
=>std::vector<int, std::allocator<int> > &
]: escriba ''std::vector<int, std::allocator<int> > &
''no se puede usar antes de''::
''porque no tiene miembros
void f3(T&&)
Mi compilador es clang 4.0.
Para mi sorpresa, f3(coll)
falla, mientras que f1(coll)
y f2(coll)
están bien.
¿Por qué una referencia de reenvío no funciona en este caso?
Debido a que T
se deduce como un tipo de referencia, debe usar std::remove_reference
template<typename T, typename = decltype(&std::remove_reference_t<T>::size)>
void f3(T&&)
{}
Ejemplo completo:
#include <vector>
#include <type_traits>
using namespace std;
template<typename T, typename = decltype(&T::size)>
void f1(T)
{}
template<typename T, typename = decltype(&T::size)>
void f2(T&)
{}
template<typename T, typename = decltype(&std::remove_reference_t<T>::size)>
void f3(T&&)
{}
int main()
{
vector<int> coll;
f1(coll); // ok
f2(coll); // ok
f3(coll); // ok
}
Generalmente, cuando se usan Referencias de Reenvío , las utilidades de modificación de tipos son muy útiles; principalmente porque las referencias de reenvío conservan tanto la categoría de valor como las calificaciones de cv
.
Ejemplo 1:
El código a continuación fails compila porque
T
se deduce comostd::vector<int>&
y no puede haber un enlace de referencia no constante a un temporal enfoo
:#include <vector> template<typename T> void foo(T&&){ T nV = {3, 5, 6}; } int main(){ std::vector<int> Vec{1, 2 ,3, 4}; foo(Vec); }
Puedes quitar la referencia para que work :
#include <vector> template<typename T> void foo(T&&){ using RemovedReferenceT = std::remove_reference_t<T>; RemovedReferenceT nV = {3, 5, 6}; } int main(){ std::vector<int> Vec{1, 2 ,3, 4}; foo(Vec); }
Ejemplo 2 (basado en el ejemplo 1):
La simple eliminación de la referencia no funcionaría en el código siguiente porque el tipo deducido tiene una calificación de
const
, (también conocido comoT
se deduce comoconst std::vector<int>&
) el nuevo tipo,RemoveReferenceT
esconst std::vector<int>
:#include <vector> template<typename T> void foo(T&&){ using RemovedReferenceT = std::remove_reference_t<T>; RemovedReferenceT nV = {3, 5, 6}; nV[2] = 7; //woopsie } int main(){ const std::vector<int> Vec{1, 2 ,3, 4}; //note the const foo(Vec); }
Podemos eliminar los calificadores
cv
del tipo de referencia eliminada.#include <vector> template<typename T> void foo(T&&){ using RRT = std::remove_reference_t<T>; using Removed_CV_of_RRT = std::remove_cv_t<RRT>; Removed_CV_of_RRT nV = {3, 5, 6}; nV[2] = 7; } int main(){ const std::vector<int> Vec{1, 2 ,3, 4}; foo(Vec); }
Podemos seguir y seguir, por la causa, podemos combine en una línea using D = std::remove_cv_t<std::remove_reference_t<T>>
, como: ==> using D = std::remove_cv_t<std::remove_reference_t<T>>
.
Aunque hay std::decay
que es realmente poderoso y short para tal "combo kick" (pero a veces quieres un poco menos de lo que hace std::decay
).