Когда я должен использовать remove_reference и add_reference? - PullRequest
7 голосов
/ 14 января 2012

Я смотрю на [VC10] unique_ptr, и они делают пару вещей, которые я не понимаю:

typedef typename tr1::remove_reference<_Dx>::type _Dx_noref;

_Dx_noref& get_deleter()
    {   // return reference to deleter
    return (_Mydel);
    }

unique_ptr(pointer _Ptr,
    typename _If<tr1::is_reference<_Dx>::value, _Dx,
        const typename tr1::remove_reference<_Dx>::type&>::_Type _Dt)
    : _Mybase(_Ptr, _Dt)
    {   // construct with pointer and (maybe const) deleter&
    }

typename tr1::add_reference<_Ty>::type operator*() const
    {   // return reference to object
    return (*this->_Myptr);
    }

Разве не просто написать _Dx & или _Ty & будет то же самое?

Я действительно понимаю, почему они сделали это здесь:

unique_ptr(pointer _Ptr, typename tr1::remove_reference<_Dx>::type&& _Dt)
    : _Mybase(_Ptr, _STD move(_Dt))
    {   // construct by moving deleter
    }

Ответы [ 3 ]

14 голосов
/ 14 января 2012

get_deleter

Любая ссылка удаляется из возвращаемого типа, затем ссылка добавляется обратно. В соответствующем C ++ 11 добавление & к существующему & (или &&) приводит к &. Однако в C ++ 03 это будет формировать ссылку на ссылочный тип, что недопустимо. Вероятно, MSVC использует старые правила, или этот код был написан, когда он это сделал, и остается потому, что он безвреден.

Конструктор

Здесь они удаляют ссылку, добавляют const, а затем добавляют ссылку обратно, чтобы передать ссылку const. Это связано с тем, что добавление const непосредственно к ссылочному типу ничего не делает ! (§8.3.2 / 1) В C ++ 11 или C ++ 03 объявление параметра было бы допустимым, но не добавило бы const, если ссылка не была удалена и заменена.

operator*

По сути, это то же самое, что и get_deleter, но они пошли другим путем, и _Ty не может быть ссылочным типом для начала. Мне кажется, что _Ty& будет достаточно, но это их прерогатива.

3 голосов
/ 14 января 2012

Вот пример, возможно, архетипический, для которого нам нужно remove_reference, в реализации std::move: цель состоит в том, чтобы возвратить ссылочный тип rvalue, основанный на выведенном выведенном типе аргумент функции.

Пример: Foo x; move(x); Здесь move(x) должен возвращать тип Foo&&. Но аргумент move является выражением типа Foo&. Так как же функция move может определить правильный тип?

Первая попытка - использовать обычное вычитание аргумента шаблона и использовать приведение:

template <typename T> T && move(??? x) { return static_cast<T&&>(x); }

Но что должно войти в ???? Если мы скажем T x, то T будет выведено как Foo&; если мы говорим T & x, то T = Foo, и если мы говорим T && x, это не будет совпадать вообще. Вторая версия, T & x, представляется полезной.

Но тогда функция не будет работать с rvalues ​​для начала (например, move(Foo(...)). В этом случае нам нужно T && x, чтобы T = Foo и T&& = Foo&& были желаемыми. Мы могли бы иметь две перегрузки, но наличие нескольких перегрузок нежелательно, поскольку без необходимости увеличивает сложность. И, наконец, если бы кто-то явно указывал параметр шаблона как move<Foo&>(x), функция никогда не работала бы, потому что когда T = Foo&, то T&& = Foo& а также.

Итак, приходит remove_reference:

template <typename T>
typename std::remove_reference<T>::type && move(T && x)
{
    return static_cast<typename std::remove_reference<T>::type &&>(x);
}

Прежде всего, новые правила свертывания ссылок подразумевают, что T выводится как Foo& или Foo&& в обоих случаях. Затем remove_reference удаляет ссылку и дает тип Foo в любом случае, а добавление && делает желаемый Foo&& тип возвращаемого значения.

В упрощенном изложении: нам нужно remove_reference, потому что (Foo&)&& равно Foo&, а не Foo&&. Если вы когда-нибудь напишите код шаблона, для которого требуется базовый тип параметра шаблона, который может быть выведен как U& или U&&, вы можете использовать эту модель.

1 голос
/ 23 мая 2013

template class add_reference имеет специализацию для void, const void и const volatile void, поскольку ссылка на void type (void&) не допускается. Если используется _Ty&, это приведет к ошибке компиляции, когда _Ty = void.

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