C’è un reference_wrapper per i riferimenti rvalue?

Mi chiedo come si possa fare quanto segue

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. } 

Come posso passare un riferimento di rvalue e memorizzarlo come riferimento di rvalue (possibilmente avvolto) nel call wrapper? So che posso scrivere manualmente una class come std::reference_wrapper che ha una funzione di conversione in T&& , ma preferirei evitarlo e usare la tecnologia Standard.


L’ho implementato come raccomandato da 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 {}; } 

Ora posso dire

 void f(string &&s) { std::string i(move(s)); /* other stuff */ } int main() { std::string s; bind(f, make_adv(move(s)))(); // Works! } 

Se passiamo un lvalue a make_adv , lo inoltreremo come un lvalue che si riferisce all’argomento di input, quindi può essere usato come sostituto di std::ref , in questo caso.

    La mia opinione su questo.

    20.8.10.1.2 / 10 in N3225

    I valori degli argomenti associati v1, v2, …, vN e i loro corrispondenti tipi V1, V2, …, VN dipendono dai tipi TiD derivati ​​dalla chiamata a bind e dai cv-qualificatori cv del call wrapper g come segue:

    • se TiD è reference_wrapper, l’argomento è tid.get () e il suo tipo Vi è T &;
    • se il valore di is_bind_expression :: value è true, l’argomento è tid (std :: forward (uj) …) e il suo tipo Vi è result_of :: type;
    • se il valore j di is_placeholder :: value non è zero, l’argomento è std :: forward (uj) e il suo tipo Vi è Uj &&;
    • altrimenti, il valore è tid e il suo tipo Vi è TiD cv &.

    Quindi l’unica possibilità di avere un riferimento di rvalue è avere is_bind_expression::value true o is_placeholder::value not zero. La seconda possibilità ha implicazioni che non si desidera e il raggiungimento del risultato desiderato con il primo implicherebbe che il problema che stiamo cercando di risolvere sia risolto se limitiamo ai tipi forniti dallo standard. Quindi, l’unica possibilità sarebbe quella di fornire il proprio wrapper e una specializzazione per is_bind_expression (che è permesso dal 20.8.10.1.1 / 1) perché non ne vedo uno.

    Come posso passare un riferimento di rvalue e memorizzarlo come riferimento di rvalue nel call wrapper?

    Il problema qui è che un object funzione di bind può essere invocato più volte. Se l’object funzione ha inoltrato un parametro associato come valore rvalore, ciò funzionerebbe ovviamente solo una volta. Quindi, questo è un po ‘un problema di sicurezza.

    Ma in alcuni casi questo tipo di inoltro è esattamente ciò che desideri. Puoi usare una lambda come intermediario:

     bind([](string& s){f(move(s));},move(s)); 

    Fondamentalmente, ho inventato questa combinazione bind + lambda come soluzione alternativa per una “cattura-cattura” mancante.

    Stavo cercando su google “reference_wrapper per rvalues” quando mi sono imbattuto in questa domanda. Non sono sicuro che la mia risposta sarebbe utile, non è legata a std :: bind e in realtà non funziona con esso, ma per altri casi d’uso potrebbe aiutare qualcuno.

    Ecco il mio tentativo di implementare 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 

    L’ultima specializzazione in namespace std consente all’implementazione della libreria standard di MSVC di funzionare con il mio tipo, ad esempio quando si utilizza 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"); 

    Credo che non sarebbe difficile implementare una simile logica “unwrapping” per altre implementazioni di libreria standard.

    Puoi usare un object lambda mutabile.

     auto func = [=]() mutable { f(std::move(s)); };