Подтвердить что ты не робот

Почему в этом случае пересылка справочной работы?

#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): примечание: шаблон кандидата игнорируется: сбой замены [с помощью 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) оба в порядке.

Почему в этом случае ссылка на пересылку не работает?

4b9b3361

Ответ 1

Поскольку 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
}

Демо


Как правило, при использовании ссылок на переадресацию тип утилиты модификации очень удобен; прежде всего потому, что ссылки пересылки сохраняют как категорию значений, так и cv qualifications.

Пример 1:

  • Код ниже не удается компилировать, потому что 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):

  • Просто удалить ссылку не работать в коде ниже, потому что выведенный тип переносит const, (aka, T выводится как const std::vector<int>&) новый тип RemoveReferenceT равен 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);
    }
    
  • Мы 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);
    }
    

Мы можем продолжать и продолжать, мы можем объединить их в одну строку, вложив их, например: == > using D = std::remove_cv_t<std::remove_reference_t<T>>.

Хотя есть std::decay, который действительно мощный и короткий для такого "комбинированного удара" (но иногда вы хотите немного меньше того, что делает std::decay).