Comments 51
Я тут посижу посмотрю.
+18
я думаю как-то так
templateT move_if_rr( T&& val )
{
return std::move(val);
}
templateconst T& move_if_rr( const T& val )
{
return val;
}
но я не уверен
templateT move_if_rr( T&& val )
{
return std::move(val);
}
templateconst T& move_if_rr( const T& val )
{
return val;
}
но я не уверен
0
хабр съел теги
templateT move_if_rr( T&& val )
{
return std::move(val);
}
templateconst T& move_if_rr( const T& val )
{
return val;
}
0
template<typename T>
T move_if_rr( T&& val )
{
return std::move(val);
}
template<typename T>
const T& move_if_rr( const T& val )
{
return val;
}
T move_if_rr( T&& val )
{
return std::move(val);
}
template<typename T>
const T& move_if_rr( const T& val )
{
return val;
}
0
Нет, не правильно
move_if_rr должна иметь как минимум 2 шаблонных аргумента (потому что тип «условия» и тип «аргумента» совсем разные):
move_if_rr должна иметь как минимум 2 шаблонных аргумента (потому что тип «условия» и тип «аргумента» совсем разные):
template< class T >
void f( T &&rr )
{
// это следует читать как
// "сделать std::move для some_arg, если rr имеет move семантику,
// иначе просто передать some_arg"
std::move_if_rr< T >( some_arg );
}
0
Почему у вас только один конструктор с параметрами? Сделайте два, один для T&& rr, другой для const T& r, и не нужно будет магии с move_if_rr(), в одном конструкторе будет обычное копирование member-а, а в другом std::move(rr.Get()), который вызовет move-конструктор у U.
Я вообще не вижу вариантов, когда был бы вызван dummy_copy, плюс я не вижу вариантов как можно написать move_if_rr с динамическим определением того rvalue это ссылка или нет, ему всегда будет приходить U&, а по одному только типу T невозможно определить тип ссылки.
Я вообще не вижу вариантов, когда был бы вызван dummy_copy, плюс я не вижу вариантов как можно написать move_if_rr с динамическим определением того rvalue это ссылка или нет, ему всегда будет приходить U&, а по одному только типу T невозможно определить тип ссылки.
0
С другой стороны, я сказал полную чушь только что, предложив вам написать templated copy constructor, да, надо либо писать по copy-constructor-у для каждого возможного U, либо вообще отказаться от templated move constructor-а, потому что это тоже, вообще говоря, неправильно.
0
Бесспорно, в нешаблонном классе, можно написать 2 конструктора, вопрос: зачем, если можно один? В случае 2х конструкторов вам придется повторить весь остальной код конструктора, не связанный с полем типа member.
0
Если простыми словами, то template Container(T&& rr) это даже не move-конструктор, это просто обычный конструктор, который принимает что-то по rvalue-ссылке, так что вопрос поставлен некорректно.
+1
тут дело в том что, если в конструкторе 1 параметр — все ок. у нас есть 2 перегрузки: const & и &&
а теперь представим что параметров стало 5, необходимое количество перегрузок станет 32, чтоб учесть все варианты вызова
для этого и была придумана шаблонная форма
то есть мы определяем одну шаблонную функцию и внутри нее при помощи forvard&move правильно обрабатываем каждый параметр
они еще обещали добавить шаблон который в компайлтайме подскажет как был передан параметр в конкретном вызове
а теперь представим что параметров стало 5, необходимое количество перегрузок станет 32, чтоб учесть все варианты вызова
для этого и была придумана шаблонная форма
то есть мы определяем одну шаблонную функцию и внутри нее при помощи forvard&move правильно обрабатываем каждый параметр
они еще обещали добавить шаблон который в компайлтайме подскажет как был передан параметр в конкретном вызове
0
Вы понимаете что шаблонных copy и move конструкторов не может существовать?
0
объясните. так как я сам только почитывал инфу на тему, сам пока не пользовался этой семантикой
0
Компилятор никогда не вызовет ваши templated copy/move конструкторы в ситуации, когда семантически должно произойти копирование, напротив, будет вызван implicitly-generated copy constructor.
Более того, сама фраза «templated copy constructor» не имеет никакого смысла, потому что как только там появляется шаблон, конструктор становится обычным, без специальной семантики, как и любой другой конвертирующий конструктор.
Плюс, если сделать «templated move constructor», то можно вообще очень сильно огрести segfault-ов.
Более того, сама фраза «templated copy constructor» не имеет никакого смысла, потому что как только там появляется шаблон, конструктор становится обычным, без специальной семантики, как и любой другой конвертирующий конструктор.
Плюс, если сделать «templated move constructor», то можно вообще очень сильно огрести segfault-ов.
+1
спасибо
0
Приведите, пожалуйста, пример кода где копилятор «не вызовет» template конструктор. Потому что, мой компилятор так не умеет.
Пример:
Это очень легко видеть под отладкой
Пример:
Container< A > a;
// вызывается Container<A>::Container<A><Container<A> >(Container<A> &&)
Container< A > b( std::move( a ) );
// вызывается Container<A>::Container<A><Container<A> &>(Container<A> & rr)
Container< A > c( a );
Это очень легко видеть под отладкой
0
Компилятор никогда не вызовет т.н. «templated copy ctor» при инициализации, можете сами проверить.
Объявив шаблонный конструктор с rvalue-ref, вы добились того, что у вас теперь никогда не будет вызван вообще никакой конструктор копирования, что уже семантическое безумие, вам не кажется? Да, благодаря reference collapsing ваш чудо-конструктор будет вызван так, как указано выше, но это не будет move-constructor-ом, вы же понимаете? Поэтому, именно семантически, это не move semantics, это магия и волшебство, а когда в коде магия и волшебство, то этот код, зачасутю, говно.
Если очень хочется извратиться, и сделать семантически некорректный код, то да, можно написать волшебную чудо-функцию move_if_rr:
template<class T, class V>
V move_if_rr(const V& value) {
return value;
}
template<class T, class V>
typename boost::disable_if<
boost::is_reference,
V&&
>::type
move_if_rr(V& value) {
return std::move(value);
}
И с её помощью написать плохой код, окей.
Объявив шаблонный конструктор с rvalue-ref, вы добились того, что у вас теперь никогда не будет вызван вообще никакой конструктор копирования, что уже семантическое безумие, вам не кажется? Да, благодаря reference collapsing ваш чудо-конструктор будет вызван так, как указано выше, но это не будет move-constructor-ом, вы же понимаете? Поэтому, именно семантически, это не move semantics, это магия и волшебство, а когда в коде магия и волшебство, то этот код, зачасутю, говно.
Если очень хочется извратиться, и сделать семантически некорректный код, то да, можно написать волшебную чудо-функцию move_if_rr:
template<class T, class V>
V move_if_rr(const V& value) {
return value;
}
template<class T, class V>
typename boost::disable_if<
boost::is_reference,
V&&
>::type
move_if_rr(V& value) {
return std::move(value);
}
И с её помощью написать плохой код, окей.
+2
Вы развиваете холивар. Конструкторы к теме move_if_rr, совершенно не при чем. То что в примере был написан конструтор, это всего лишь случайность, замените в уме конструктор на обычную forward-функцию.
Касательно конструкторов. Да, шаблонный конструктор не заменит стандартный, однако неверно говорить, что он какой-либо из них никогда не будет вызван. Если быть точным, то стандартный конструктор копирования для типа Т будет вызван если явно привести аргумент к типу «const T &», в остальных случаях будет вызван шаблонный конструктор. В случае, если шаблонный конструктор ведет себя так же, как и стандартный ничего страшного в этом нет. Впрочем, согласен, так лучше не делать. Но, как, я уже говорил, это был всего лишь пример.
Что касается вашей функции, то есть только одно замечание: возвращаемое значение в первой перегрузке должно быть «const V&», а не «V»: семантически std::move и std::forward ничего не копируют, они просто делают преобразование типа ссылки, поэтому move_if_rr, как расширение std::forward, тоже должна заниматься только этим.
Касательно конструкторов. Да, шаблонный конструктор не заменит стандартный, однако неверно говорить, что он какой-либо из них никогда не будет вызван. Если быть точным, то стандартный конструктор копирования для типа Т будет вызван если явно привести аргумент к типу «const T &», в остальных случаях будет вызван шаблонный конструктор. В случае, если шаблонный конструктор ведет себя так же, как и стандартный ничего страшного в этом нет. Впрочем, согласен, так лучше не делать. Но, как, я уже говорил, это был всего лишь пример.
Что касается вашей функции, то есть только одно замечание: возвращаемое значение в первой перегрузке должно быть «const V&», а не «V»: семантически std::move и std::forward ничего не копируют, они просто делают преобразование типа ссылки, поэтому move_if_rr, как расширение std::forward, тоже должна заниматься только этим.
0
Ну, я про это и говорил: в контексте, когда должен быть неявно вызван конструктор копирования, он вызван не будет и нужно применять специальное волшебство, чтобы восстановить нормальную семантику, а это не дело.
А функцию я на коленке написал, там ещё шаблонный параметр пропущен, да.
А функцию я на коленке написал, там ещё шаблонный параметр пропущен, да.
0
Шаблонных move и copy конструкторов действительно не может существовать. Могут существовать шаблонные конструкторы преобразования или просто шаблонные конструкторы.
Пример.
У вас есть класс вектора.
Вы хотите написать конструктор, который будет конструировать из вектора другого типа, при этом вы хотите для каждого элемента вызвать конструктор копирования или перемещения в зависимости от того переместили ли этот другой векор в вас или скопировали. move_if_rr решает как раз эту задачу.
Причина возникновения «другого» типа вектора, может быть банальна: он каким-то образом оптимизирован для конкретных задач.
Пример.
У вас есть класс вектора.
Вы хотите написать конструктор, который будет конструировать из вектора другого типа, при этом вы хотите для каждого элемента вызвать конструктор копирования или перемещения в зависимости от того переместили ли этот другой векор в вас или скопировали. move_if_rr решает как раз эту задачу.
Причина возникновения «другого» типа вектора, может быть банальна: он каким-то образом оптимизирован для конкретных задач.
+1
в этом cpp-next.com блоге есть пост с огромным холиваром на эту тему, но я не помню какой именно
0
Вопрос поставлен корректно.
Читайте UPD2, я написал, зачем нужна такая функция.
Читайте UPD2, я написал, зачем нужна такая функция.
0
Надеюсь, подобный код вы активно используете в production.
+6
Так ведь тут нету ничего сложного :)
Поробуйте открыть boost или stl :)
Поробуйте открыть boost или stl :)
0
Есть как бы разница между кодом, который пишут для компилятора, и кодом, который пишут для людей.
Какой-нибудь boost::mpl или внутренняя реализация stl'я — это из первой категории. Если также выглядит ваш обычный код — мне жаль и вас и ваших коллег.
Какой-нибудь boost::mpl или внутренняя реализация stl'я — это из первой категории. Если также выглядит ваш обычный код — мне жаль и вас и ваших коллег.
+2
если дальше продолжать ваши рассуждения то окажется что профессиональный рост фтопку.
когда человеку приходится работать с таким кодом, он понимает его суть, что как бы увеличивает его профессионализм, использовать подобный код самому или нет это уже другое решение, которое будет принято позже с учетом опыта и набитых шишек
.
ориентация на слабых обычно не приводит ни к чему хорошему.
когда человеку приходится работать с таким кодом, он понимает его суть, что как бы увеличивает его профессионализм, использовать подобный код самому или нет это уже другое решение, которое будет принято позже с учетом опыта и набитых шишек
.
ориентация на слабых обычно не приводит ни к чему хорошему.
0
Пока получился вот такой вариант:
template<typename T> struct helper
{
template<typename U> struct result
{
typedef U&& type;
};
template<typename U> static U&& get( U& u )
{
return static_cast<U&&>(u);
}
};
template<typename T> struct helper<T&>
{
template<typename U> struct result
{
typedef U const& type;
};
template<typename U> static U const& get( U const& u )
{
return u;
}
};
template<typename T, typename U>
typename helper<T>::template result<U>::type move_if_rr( U& v )
{
return helper<T>::get( v );
}
0
> успех засчитывается за функцию, которая работает правильно при условии, что Вы ее ни разу не отлаживали
Не учел… Несколько раз компилировал (проверить синтаксические ошибки)…
Не учел… Несколько раз компилировал (проверить синтаксические ошибки)…
0
Можно сократить:
template<typename T, typename U> struct helper
{
typedef U&& result;
static U&& get( U& u )
{
return static_cast<U&&>( u );
}
};
template<typename T, typename U> struct helper<T&, U>
{
typedef U const& result;
static U const& get( U const& u )
{
return u;
}
};
template<typename T, typename U>
typename helper<T,U>::result move_if_rr( U& u )
{
return helper<T,U>::get( u );
}
0
Будет работать неправильно в ряде случаев:
Container c0;
const Container& get_container() {
return c0;
}
Container c1(get_container());
Будет вызван implicit copy constructor.
0
Парсер съел шаблоны, но, думаю, и так понятно о чем речь.
0
> Будет работать неправильно в ряде случаев:
Это не относится к реализации move_if_rr.
Уже не один раз в этой теме писали, что конструкция
не работает в качестве конструктора копирования.
Это не относится к реализации move_if_rr.
Уже не один раз в этой теме писали, что конструкция
template< class T > Container( T&& );
не работает в качестве конструктора копирования.
0
Я перефразировал условие, конструкторы попали под раздачу совершенно случайно.
Что касается реализации. Она работает, но с оговорками: move работает правильно, а вот с константностью беда, например, у вас (в типах):
«const B & == move_if_rr< A & >( B & )»
в данном случае не должно появляться константы в результате
Что касается реализации. Она работает, но с оговорками: move работает правильно, а вот с константностью беда, например, у вас (в типах):
«const B & == move_if_rr< A & >( B & )»
в данном случае не должно появляться константы в результате
0
> а вот с константностью беда
Не учел.
Убираем const из специализации шаблона helper. Получаем:
Комментируем или правим четвертый блок static_assert-ов в представленном тесте.
Тест проходит.
Что касается четвертого блока assert-ов (для /**/const A /**/).
Непонятно почему все возвращаемые типы имеют &, а не &&?
Не учел.
Убираем const из специализации шаблона helper. Получаем:
template<typename T, typename U> struct helper
{
typedef U&& result;
static U&& get( U& u ) { return static_cast<U&&>( u ); }
};
template<typename T, typename U> struct helper<T&, U>
{
typedef U& result;
static U& get( U& u ) { return u;}
};
template<typename T, typename U> typename helper<T,U>::result move_if_rr( U& u )
{
return helper<T,U>::get( u );
}
Комментируем или правим четвертый блок static_assert-ов в представленном тесте.
Тест проходит.
Что касается четвертого блока assert-ов (для /**/const A /**/).
Непонятно почему все возвращаемые типы имеют &, а не &&?
0
> Что касается четвертого блока assert-ов (для /**/const A /**/).
> Непонятно почему все возвращаемые типы имеют &, а не &&?
Это именно те грабли.
Возьмем функцию:
«move_if_rr{ FWD }( arg )» — выполнить move для arg, если FWD аргумент имеет move семантику, в противном случае ничего не делать. «Ничего не делать» и есть эквивалент отказу от rvalue-ссылки. Почему FWD в случае FWD = «const Dummy» не имеет move семантики мы разобрались выше.
> Непонятно почему все возвращаемые типы имеют &, а не &&?
Это именно те грабли.
Возьмем функцию:
template< class T >Вызовем ее так:
void Fwd( T &&rr )
{
g( std::forward< T >( rr ) );
}
const Dummy o;В данном случае T == «const Dummy», при этом g будет вызвана с «const Dummy &&», однако «const T &&» — это нифига не rvalue-ссылка на Dummy (мы не можем ничего «переместить» из константного объекта). Т.е. «move_if_rr{ const Dummy }( arg )» не должна перемещать arg. Этого можно добиться либо отказом от rvalue-ссылки (как у меня), либо добавлением модификатора const к типу arg перед оборачиванием его в rvalue ссылку. Семантически это одно и то же, если только у вас нету специализации «g» именно для «const Dummy &&», что само по себе несколько странно. Выбор в пользу отказа от rvalue ссылки сделан исходя из названия функции:
Fwd( std::move( o ) );
«move_if_rr{ FWD }( arg )» — выполнить move для arg, если FWD аргумент имеет move семантику, в противном случае ничего не делать. «Ничего не делать» и есть эквивалент отказу от rvalue-ссылки. Почему FWD в случае FWD = «const Dummy» не имеет move семантики мы разобрались выше.
0
С учетом представленного просто добавляем строчку
Весь код:
Тест пройден.
Много написанного, но хотелось довести данный вариант до конца. Просто не всегда хочется иметь зависимости от дополнительных библиотек.
template<typename T, typename U> struct helper<T const, U> : helper<T&, U> {};
Весь код:
template<typename T, typename U> struct helper
{
typedef U&& result;
static U&& get( U& u ) { return static_cast<U&&>( u ); }
};
template<typename T, typename U> struct helper<T&, U>
{
typedef U& result;
static U& get( U& u ) { return u;}
};
template<typename T, typename U> struct helper<T const, U> : helper<T&, U> {};
template<typename T, typename U> typename helper<T,U>::result move_if_rr( U& u )
{
return helper<T,U>::get( u );
}
Тест пройден.
Много написанного, но хотелось довести данный вариант до конца. Просто не всегда хочется иметь зависимости от дополнительных библиотек.
0
Можно, конечно, вынести static_cast в реализацию move_if_rr (как в представленном автором решении):
template<typename T, typename U> struct helper
{ typedef U&& result; };
template<typename T, typename U> struct helper<T&, U>
{ typedef U& result; };
template<typename T, typename U> struct helper<T const, U>
: helper<T&, U> {};
template<typename T,typename U> typename helper<T,U>::result move_if_rr(U&u)
{
return static_cast<helper<T,U>::result>( u );
}
+1
Пройдет ещё немного времени, и плюсовики совсем перестанут решать реальные проблемы, а будут думать, как же правильно заюзать все фичи языка с этим безумным синтаксисом. Здесь что-то не так. :)
0
Всё просто: чем сложнее задачи — тем сложнее инструмент.
0
Задача «вертикально забить трубу в землю» сложнее, чем «забить гвоздь», но рабочие на стройке не пытаются собрать трубозабиватель на месте — они используют уже готовые, на которых работать не сильно сложнее, чем работать с молотком.
Инструментарий с задачами усложняется, но обычно не экспоненциально, как С++. :)
Инструментарий с задачами усложняется, но обычно не экспоненциально, как С++. :)
-1
Если суть задачи состоит в том, чтобы у rvalue-объектов можно было «утащить» внутренние члены-данные через аксессоры, а с lvalue-объектами работать как обычно, то словами формулировку задачи можно описать так:
Реализовать функцию для вызова в виде move_if_rr<A>(b), которая вернет rvalue-ссылку на значение b, если тип A является rvalue-ссылкой, иначе вернет b как есть. Тогда так и запишем, слово за словом, используя STL:
В такой формулировке утилитная функция может быть довольно полезна для перемещения внутреннего свойства из временного объекта, вместо его копирования.
А еще полезнее она была бы в более общем виде:
move_if(val) — выполнить перемещение, если condition истинно, иначе — оставить тип как есть. В нашем случае использование:
Но, судя по тестовому коду и static_assert'ам, задачу нужно было решить в другом виде.
Реализовать функцию для вызова в виде move_if_rr<A>(b), которая вернет rvalue-ссылку на значение b, если тип A является rvalue-ссылкой, иначе вернет b как есть. Тогда так и запишем, слово за словом, используя STL:
#define RET_TYPE \
typename std::conditional< \
std::is_rvalue_reference<E>::value \
, typename std::remove_reference<I>::type&& \
, I&& \
>::type
template <typename E, typename I>
RET_TYPE move_if_rr(I&& i)
{
return static_cast<RET_TYPE>(i);
}
В такой формулировке утилитная функция может быть довольно полезна для перемещения внутреннего свойства из временного объекта, вместо его копирования.
А еще полезнее она была бы в более общем виде:
#define RET_TYPE \
typename std::conditional< \
C \
, typename std::remove_reference<I>::type&& \
, I&& \
>::type
template <bool C, typename I>
RET_TYPE move_if(I&& i)
{
return static_cast<RET_TYPE>(i);
}
move_if(val) — выполнить перемещение, если condition истинно, иначе — оставить тип как есть. В нашем случае использование:
move_if<std::is_rvalue_reference<A>::value>(b);
Но, судя по тестовому коду и static_assert'ам, задачу нужно было решить в другом виде.
0
Хотя, рантайм проверка на Вашем юнит-тесте проходит, если исправить строку
Сильно не ругайте, это мое видение постановки и решения задачи.
member = move_if_rr< T >( rr.Get() );
наmember = move_if_rr< T&& >( rr.Get() );
, что как раз укладывается в моем представлении. Ведь rr имеет тип T&&, а не T.Сильно не ругайте, это мое видение постановки и решения задачи.
0
Даже если исправить static assert-ы (в расчете на то, что нужно указывать как параметр T&&, а не T&), статический тест не пройдет, провалятся следующие проверки:
1. (спорная) Если FWD не является «полноценной rvalue ссылкой», вы не удаляете возможно присутствующую rvalue ссылку из Arg. Проблема спорная потому, что move_if_rr подразумевает «ничего не делать», если аргумент не является «полноценной rvalue ссылкой», а мы тут ссылку убираем. Однако, я считаю такое поведение правильным, т.к. оно подчеркивает, что move будет выполнен ТОЛЬКО если в качестве FWD выступает «полноценная rvalue ссылка», вне зависимости от того, передали ли Arg через std::move или нет. В пользу этого подхода выступает тот факт, что std::forward ведет себя точно также: его поведение не меняется от того, передать ему аргумент через std::move или нет.
2. Вы заметили, что в предыдущем пункте я оперирую понятием «полноценная rvalue ссылка», так вот, если есть класс A, то «A &&» — это «полноценная rvalue ссылка», а вот «const A &&» — нет. Почему? Потому что вы никак не можете что-то переместить из константного объекта, ведь его для этого надо как минимум изменить.
Почему код «прошел» рантайм тест?
Рантайм тест был направлен на проверку того, что функция не упадет с каким-нибудь page fault, семантика тестировалась статическим тестом. Обе эти ситуации можно отловить и в рантайме: первая произойдет, если в код «memver = move_if_rr{ T && }( rr.Get() )» заменить на «memver = move_if_rr{ T && }( std::move( rr.Get() ) )», а вторая — если разрешить константному Container возвращать не константный member (move конструктор для member не вызвался только потому, что в конструктор передали «const A &&», а не «А &&»).
typedef A T1;По сути у вас 2 проблемы, одна из которых спорная:
typedef A &T2;
typedef const A &T3;
typedef const A T4;
// move_if_rr< FWD >( Arg ) = Res
//
// Я буду писать так:
// ( FWD, Arg ) ? Res
// Следующие выражения не будут верны:
// 1. ( T2 &&, const B && ) == const B & (Ваш код даст const B &&)
// 2. ( T2 &&, B && ) == B & (Ваш код даст B &&)
// 3. ( T3 &&, const B && ) == const B & (Ваш код даст const B &&)
// 4. ( T3 &&, B && ) == B & (Ваш код даст B &&)
// 5. ( T4 &&, const B && ) == const B & (Ваш код даст const B &&)
// 6. ( T4 &&, const B & ) == const B & (Ваш код даст const B &&)
// 7. ( T4 &&, B && ) == B & (Ваш код даст B &&)
// 8. ( T4 &&, B & ) == B & (Ваш код даст B &&)
1. (спорная) Если FWD не является «полноценной rvalue ссылкой», вы не удаляете возможно присутствующую rvalue ссылку из Arg. Проблема спорная потому, что move_if_rr подразумевает «ничего не делать», если аргумент не является «полноценной rvalue ссылкой», а мы тут ссылку убираем. Однако, я считаю такое поведение правильным, т.к. оно подчеркивает, что move будет выполнен ТОЛЬКО если в качестве FWD выступает «полноценная rvalue ссылка», вне зависимости от того, передали ли Arg через std::move или нет. В пользу этого подхода выступает тот факт, что std::forward ведет себя точно также: его поведение не меняется от того, передать ему аргумент через std::move или нет.
2. Вы заметили, что в предыдущем пункте я оперирую понятием «полноценная rvalue ссылка», так вот, если есть класс A, то «A &&» — это «полноценная rvalue ссылка», а вот «const A &&» — нет. Почему? Потому что вы никак не можете что-то переместить из константного объекта, ведь его для этого надо как минимум изменить.
Почему код «прошел» рантайм тест?
Рантайм тест был направлен на проверку того, что функция не упадет с каким-нибудь page fault, семантика тестировалась статическим тестом. Обе эти ситуации можно отловить и в рантайме: первая произойдет, если в код «memver = move_if_rr{ T && }( rr.Get() )» заменить на «memver = move_if_rr{ T && }( std::move( rr.Get() ) )», а вторая — если разрешить константному Container возвращать не константный member (move конструктор для member не вызвался только потому, что в конструктор передали «const A &&», а не «А &&»).
0
Касательно, названия move_if_rr, мне кажется, Вы его интерпретировали как «move_if_rvalue_reference», однако это не так.
Ноги растут отсюда:
Если не использовать форвардинг, то для достижения того же эффекта, что и с ним, нужно объявить минимум 2 функции:
void f( const A &rc );
void f( A &&rr );
С форвардингом это выглядит так:
templatevoid f( T &&rr );
Так вот rr в названии отсылает к rr в «void f( A &&rr );»:
«Переместить, если бы в отсутствии форвардинга вызывалась функция которая с rr»
Я согласен, что название не очень удачное, но лучше идей у меня до сих пор не появилось.
Ноги растут отсюда:
Если не использовать форвардинг, то для достижения того же эффекта, что и с ним, нужно объявить минимум 2 функции:
void f( const A &rc );
void f( A &&rr );
С форвардингом это выглядит так:
templatevoid f( T &&rr );
Так вот rr в названии отсылает к rr в «void f( A &&rr );»:
«Переместить, если бы в отсутствии форвардинга вызывалась функция которая с rr»
Я согласен, что название не очень удачное, но лучше идей у меня до сих пор не появилось.
0
Sign up to leave a comment.
А понимаете ли Вы move семантику?