Существует ли 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:

 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 в этом случае.

Я принимаю это.

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::value true или is_placeholder::value не равно нулю. Вторая возможность имеет последствия, которые вы не хотите, и достижение желаемого результата с первым будет означать, что проблема, которую мы пытаемся решить, решена, если мы ограничимся стандартными предоставленными типами. Таким образом, единственная возможность – предоставить вашу собственную оболочку и специализацию для 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)); }; 
  • Что является прецедентом для перегрузки функций-членов в ссылочных квалификаторах?
  • Что означает T && (double ampersand) в C ++ 11?
  • Правило-тройка становится Правилом пяти с C ++ 11?
  • Могу я обычно / всегда использовать std :: forward вместо std :: move?
  • В C ++ какие категории (lvalue, rvalue, xvalue и т. Д.) Могут выражать выражения, которые производят временные classы типа classа?
  • Передача rvalues ​​через std :: bind
  • Разрешение перегрузки между объектом, ссылкой rvalue, константой ссылки
  • C ++ 11 make_pair с указанными параметрами шаблона не компилируется
  • Нужны ли ссылки rvalue на const?
  • Давайте будем гением компьютера.