17
#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):注意:候选模板被忽略:替换失败 [with T= > std::vector<int, std::allocator<int> > &]:类型“ std::vector<int, std::allocator<int> > &”不能在“ ::”之前使用,因为它没有成员

void f3(T&&)

我的编译器是clang 4.0。

令我惊讶的是,f3(coll)失败了,而f1(coll)两者f2(coll)都还可以。

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

4

1 回答 1

21

因为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
}

演示


通常,在使用Forwarding References时,类型修改实用程序非常方便;主要是因为转发引用保留了值类别cv限定

示例 1:

  • 下面的代码无法编译,因为T推断为std::vector<int>&并且您不能将非常量引用绑定到临时 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);
    }
    
  • 您可以删除引用以使其正常工作

    #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):

  • 简单地删除引用在下面的代码中是行不通的,因为推导的类型带有一个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);
    }
    
  • 我们可以从removed-reference 的类型中删除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);
    }
    

我们可以继续下去,当然,我们可以通过嵌套它们组合在一行中,例如: ==> using D = std::remove_cv_t<std::remove_reference_t<T>>

尽管对于这种“组合踢”来说std::decay,这确实非常强大且简短(但有时您想要的功能少一点std::decay

于 2017-02-23T07:52:34.213 回答