c++ c++11 templates overloading forwarding-reference

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 }

Demo

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 como std::vector<int>& y no puede haber un enlace de referencia no constante a un temporal en foo :

    #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 como T se deduce como const std::vector<int>& ) el nuevo tipo, RemoveReferenceT es const 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 ).