简体   繁体   中英

Why doesn't forwarding reference work in this case?

#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): note: candidate template ignored: substitution failure [with T = > std::vector<int, std::allocator<int> > & ]: type ' std::vector<int, std::allocator<int> > & ' cannot be used prior to ' :: ' because it has no members

void f3(T&&)

My compiler is clang 4.0.

To my surprise, f3(coll) fails, while f1(coll) and f2(coll) are both ok.

Why does a forwarding reference not work in this case?

Because T is deduced as a reference type, you need to use std::remove_reference

template<typename T, typename = decltype(&std::remove_reference_t<T>::size)>
void f3(T&&)
{}

Full example:

#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


Generally, when using Forwarding References , type modification utilities comes in very handy; primarily because forwarding references preserves both value category and cv qualifications .

Example 1:

  • The code below fails to compile because T is deduced as std::vector<int>& and you cannot have a non-const reference bind to a temporary in 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); } 
  • You can remove the reference to get it to 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); } 

Example 2 (builds upon example 1):

  • Simply removing the reference would not work in the code below because the deduced type carries a const qualification, (aka, T is deduced as const std::vector<int>& ) the new type, RemoveReferenceT is 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); } 
  • We can remove the cv qualifiers from the removed-reference's type.

     #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); } 

We can go on and on, of cause, we can combine them in one line by nesting them, like: ==> using D = std::remove_cv_t<std::remove_reference_t<T>> .

Though there is std::decay that is really powerful and short for such "combo kick" (but sometimes you want a little less of what std::decay does).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM