为什么转发参考在这种情况下不起作用?

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&&)

我的编译器是 clang 4.0。

令我惊讶的是,f3(coll) 失败了,而 f1(coll)f2(coll) 都正常。

为什么转发引用在这种情况下不起作用?

因为T被推导为引用类型,所以需要使用std::remove_reference

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

完整示例:

#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


通常,当使用转发引用时type modification utilities comes in very handy; primarily because forwarding references preserves both value category and cv qualifications

示例 1:

  • fails 下面的代码要编译,因为 T 被推导为 std::vector<int>& 并且你不能有一个 非常量引用 绑定到临时 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);
    }
    
  • 您可以删除引用以将其获取到 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);
    }
    

示例 2(基于示例 1):

  • 简单地删除引用将在下面的代码中 not work 因为推导的类型带有 const 限定,(又名,T 被推导为 const std::vector<int>&) 新类型,RemoveReferenceTconst 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);
    }
    
  • 我们 can 从已删除引用的类型中删除 cv 限定符。

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

我们可以继续下去,因为我们可以通过嵌套将它们 combine 放在一行中,例如:==> using D = std::remove_cv_t<std::remove_reference_t<T>>.

尽管有 std::decay 所做的 std::decay that is really powerful and short for such "combo kick" (but sometimes you want a little less