Существует ли reference_wrapper для ссылок rvalue?
Интересно, как можно сделать следующее
void f(string &&s) { std::string i(move(s)); /* other stuff */ } int main() { std::string s; bind(f, s)(); // Error. bind(f, move(s))(); // Error. bind(f, ref(s))(); // Error. }
Как я могу передать ссылку rvalue и сохранить ее как ссылку rvalue (возможно, завернутую) в оболочку вызова? Я знаю, что могу вручную написать class типа std::reference_wrapper
который имеет функцию преобразования в T&&
, но я бы предпочел избежать этого и использовать стандартную технологию.
Я реализовал его, как рекомендует AProgrammer:
- Как реализовать правило пяти?
- Можно ли std :: переместить объекты из функций? (C ++ 11)
- неclassовые значения всегда имеют cv-неквалифицированные типы
- Почему некоторые люди используют swap для переадресации?
- Ссылка Rvalue обрабатывается как Lvalue?
template struct adv { T t; explicit adv(T &&t):t(forward(t)) {} template T &&operator()(U &&...) { return forward(t); } }; template adv make_adv(T &&t) { return adv{forward(t)}; } namespace std { template struct is_bind_expression< adv > : std::true_type {}; }
Теперь я могу сказать
void f(string &&s) { std::string i(move(s)); /* other stuff */ } int main() { std::string s; bind(f, make_adv(move(s)))(); // Works! }
Если мы передадим значение lvalue в make_adv
, он пересылает его как lvalue, ссылаясь на входной аргумент, поэтому он может использоваться в качестве замены для std::ref
в этом случае.
- Переместить захват в lambda
- Почему `std :: move` называется` std :: move`?
- О том, как распознать ссылку Rvalue или Lvalue и правило if-it-have-a-name
- Является ли возrotation ссылкой rvalue более эффективным?
- Есть ли случай, когда полезно использовать ссылку RValue Reference (&&)?
- Синтаксис для универсальных ссылок
- C ++ 11 rvalues и перемещение семантики путаницы (return statement)
- Преимущества использования передовой
Я принимаю это.
20.8.10.1.2 / 10 в N3225
Значения связанных аргументов v1, v2, …, vN и их соответствующих типов V1, V2, …, VN зависят от типов TiD, полученных от вызова привязки, и cv-квалификаторов cv оболочки вызова g следующим образом:
- если TiD является reference_wrapper, аргументом является tid.get (), а его тип Vi – T &;
- если значение is_bind_expression :: value равно true, аргумент равен tid (std :: forward (uj) …), а его тип Vi – result_of :: type;
- если значение j is_placeholder :: value не равно нулю, аргумент std :: forward (uj), а его тип Vi – Uj &&;
- в противном случае значение tid и его тип Vi есть TiD cv &.
Таким образом, единственная возможность иметь ссылку rvalue – иметь is_bind_expression
true или is_placeholder
не равно нулю. Вторая возможность имеет последствия, которые вы не хотите, и достижение желаемого результата с первым будет означать, что проблема, которую мы пытаемся решить, решена, если мы ограничимся стандартными предоставленными типами. Таким образом, единственная возможность – предоставить вашу собственную оболочку и специализацию для is_bind_expression
(что разрешено 20.8.10.1.1 / 1), поскольку я ее не вижу.
Как передать ссылку rvalue и сохранить ее как ссылку rvalue в обертке вызова?
Проблема здесь в том, что такой объект функции bind может быть вызван несколько раз. Если объект функции перенаправил связанный параметр как rvalue, это, очевидно, будет работать только один раз. Таким образом, это немного проблема безопасности.
Но в некоторых случаях такой вид пересылки – именно то, что вы хотите. Вы можете использовать lambda в качестве посредника:
bind([](string& s){f(move(s));},move(s));
В принципе, я придумал эту комбинацию bind + lambda в качестве обходного пути для отсутствия «захвата движения».
Когда я наткнулся на этот вопрос, я искал «reference_wrapper для rvalues». Не уверен, будет ли мой ответ полезен, он не связан с std :: bind и на самом деле не работает с ним, но для некоторых других случаев использования он может помочь кому-то.
Вот моя попытка реализовать rvalue_reference_wrapper:
#pragma once #include #include #include template class rvalue_reference_wrapper { public: static_assert(::std::is_object::value, "rvalue_reference_wrapper requires T to be an object type."); using type = T; rvalue_reference_wrapper(T& ref_value) = delete; rvalue_reference_wrapper(T&& ref_value) noexcept : _pointer(::std::addressof(ref_value)) { } operator T&&() && noexcept { return ::std::move(*_pointer); } T&& get() && noexcept { return ::std::move(*_pointer); } template auto operator()(ArgTypes&&... args) && -> decltype(::std::invoke(::std::declval>().get(), ::std::forward(args)...)) { return (::std::invoke(::std::move(*this).get(), ::std::forward (args)...)); } private: T* _pointer; }; template inline rvalue_reference_wrapper rv_ref(T& ref_value) = delete; template inline ::std::enable_if_t::value), rvalue_reference_wrapper > rv_ref(T&& ref_value) noexcept { return rvalue_reference_wrapper (::std::forward (ref_value)); } #ifdef _MSC_VER namespace std { template struct _Unrefwrap_helper> { using type = T &&; static constexpr bool _Is_refwrap = true; }; } #else #pragma error("TODO : implement...") #endif
Последняя специализация в пространстве имен std позволяет MSVC реализовать стандартную библиотеку для работы с моим типом, например, при использовании std :: make_tuple:
int a = 42; auto p_int = std::make_unique(42); auto test_tuple = std::make_tuple(42, std::ref(a), rv_ref(std::move(p_int))); static_assert(std::is_same&&>>::value, "unexpected result");
Я считаю, что было бы непросто реализовать подобную «развернутую» логику для других стандартных реализаций библиотек.
Вы можете использовать изменяемый lambda-объект.
auto func = [=]() mutable { f(std::move(s)); };