Предотвращение преобразования неконстантных lvalues ​​в ссылку на rvalue вместо константной lvalue - PullRequest
19 голосов
/ 13 октября 2011

У меня проблемы с перегрузкой функции для получения значения либо по константной ссылке, либо, если это rvalue, по ссылке rvalue.Проблема в том, что мои неконстантные значения привязаны к версии функции.Я делаю это в VC2010.

#include <iostream>
#include <vector>

using namespace std;

template <class T>
void foo(const T& t)
{cout << "void foo(const T&)" << endl;}

template <class T>
void foo(T&& t)
{cout << "void foo(T&&)" << endl;}

int main()
{
    vector<int> x;
    foo(x); // void foo(T&&) ?????
    foo(vector<int>()); // void foo(T&&)
}

Приоритет, кажется, состоит в том, чтобы вывести foo (x) как

foo< vector<int> & >(vector<int>& && t)

вместо

foo< vector<int> >(const vector<int>& t)

Iпопытался заменить ссылочную версию rvalue на

void foo(typename remove_reference<T>::type&& t)

, но это только привело к тому, что все превратилось в эталонную версию const-lvalue.

Как предотвратить такое поведение?И в любом случае, почему это значение по умолчанию - это кажется настолько опасным, учитывая, что ссылки на rvalue разрешено изменять, и это оставляет меня с неожиданно измененной локальной переменной.

РЕДАКТИРОВАТЬ: Просто добавили не шаблонные версии функцийи они работают как положено.Делать функцию шаблоном изменяет правила разрешения перегрузки?Это .. действительно расстраивает!

void bar(const vector<int>& t)
{cout << "void bar(const vector<int>&)" << endl;}

void bar(vector<int>&& t)
{cout << "void bar(vector<int>&&)" << endl;}

bar(x); // void bar(const vector<int>&)
bar(vector<int>()); // void bar(vector<int>&&)

Ответы [ 2 ]

24 голосов
/ 13 октября 2011

Когда у вас есть подобная шаблонная функция, вы почти никогда не хотите перегружаться.Параметр T&& является параметром catch что-либо .И вы можете использовать его, чтобы получить любое поведение из перегрузки one .

#include <iostream>
#include <vector>

using namespace std;

template <class T>
void display()
{
    typedef typename remove_reference<T>::type Tr;
    typedef typename remove_cv<Tr>::type Trcv;
    if (is_const<Tr>::value)
        cout << "const ";
    if (is_volatile<Tr>::value)
        cout << "volatile ";
    std::cout << typeid(Trcv).name();
    if (is_lvalue_reference<T>::value)
        std::cout << '&';
    else if (is_rvalue_reference<T>::value)
        std::cout << "&&";
    std::cout << '\n';
}

template <class T>
void foo(T&& t)
{
    display<T>();
}

int main()
{
    vector<int> x;
    vector<int> const cx;
    foo(x); // vector<int>&
    foo(vector<int>()); // vector<int>
    foo(cx);  // const vector<int>&
}
12 голосов
/ 13 октября 2011

Чтобы T&& связывался с ссылкой lvalue, T сам должен быть типом ссылки lvalue.Вы можете запретить создание шаблона с типом ссылки T:

enable_if находится в <utility>;is_reference находится в <type_traits>.

Причина, по которой перегрузка, принимающая T&&, предпочтительнее, чем перегрузка, принимающая T const&, состоит в том, что T&& является точным соответствием (с T = vector<int>&)но T const& требует преобразования квалификации (необходимо добавить const-квалификацию).

Это происходит только с шаблонами.Если у вас есть нешаблонная функция, которая принимает std::vector<int>&&, вы сможете вызывать эту функцию только с аргументом rvalue.Если у вас есть шаблон, который принимает T&&, вы не должны думать о нем как о «ссылочном параметре rvalue»;это «универсальный эталонный параметр» (по-моему, Скотт Мейерс использовал похожий язык).Он может принимать все, что угодно.

Разрешение параметру T&& шаблона функции связываться с любой категорией аргумента - это то, что обеспечивает идеальную пересылку.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...