我想知道如何做以下事情
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.
}
如何传递右值引用并将其存储为调用包装器中的右值引用(可能包装)?我知道我可以手动编写类似std::reference_wrapper<>
的类,其转换函数为T&&
,但我宁愿避免使用标准技术。
我像AProgrammer推荐的那样实现了它:
template<typename T> struct adv {
T t;
explicit adv(T &&t):t(forward<T>(t)) {}
template<typename ...U> T &&operator()(U &&...) {
return forward<T>(t);
}
};
template<typename T> adv<T> make_adv(T &&t) {
return adv<T>{forward<T>(t)};
}
namespace std {
template<typename T>
struct is_bind_expression< adv<T> > : 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!
}
如果我们将左值传递给make_adv
,它会将其转发为引用输入参数的左值,因此在这种情况下,它可以用作std::ref
的替换值。
答案 0 :(得分:7)
我对此的看法。
N3225中的20.8.10.1.2 / 10
绑定参数v1,v2,...,vN的值及其对应的类型V1,V2,...,VN 取决于从bind调用派生的TiD类型和调用包装器g的cv-qualifiers cv 如下:
- 如果TiD是reference_wrapper,则参数为tid.get(),其类型Vi为T&amp ;;
- 如果is_bind_expression :: value的值为true,则参数为tid(std :: forward(uj)...) 它的类型Vi是result_of :: type;
- 如果is_placeholder :: value的值j不为零,则参数为std :: forward(uj) 它的类型Vi是Uj&amp;&amp ;;
- 否则,该值为tid,其类型Vi为TiD cv&amp;。
因此,拥有右值引用的唯一可能性是is_bind_expression<TiD>::value
为真或is_placeholder<TiD>::value
不为零。第二种可能性具有您不想要的含义并且实现想要的结果,第一种可能性意味着如果我们限制标准提供的类型,我们试图解决的问题就会得到解决。因此,唯一的可能性是提供您自己的包装器和is_bind_expression<TiD>
的特化(由20.8.10.1.1 / 1允许),因为我没有看到。
答案 1 :(得分:6)
如何传递rvalue引用并将其作为rvalue引用存储在调用包装器中?
这里的问题是可以多次调用这样的绑定函数对象。如果函数对象将绑定参数转发为rvalue,那么这显然只能工作一次。所以,这是一个安全问题。
但在某些情况下,这种转发正是您想要的。您可以使用lambda作为中介:
bind([](string& s){f(move(s));},move(s));
基本上,我想出了这个绑定+ lambda组合作为缺少“移动捕获”的解决方法。
答案 2 :(得分:2)
我正在谷歌搜索&#34; reference_wrapper for rvalues&#34;当我偶然发现这个问题时。 不确定我的答案是否有用,它与std :: bind无关,实际上并没有使用它,但对于其他一些用例,它可能对某人有用。
这是我尝试实现rvalue_reference_wrapper:
#pragma once
#include <type_traits>
#include <memory>
#include <utility>
template<class T>
class rvalue_reference_wrapper
{
public:
static_assert(::std::is_object<T>::value, "rvalue_reference_wrapper<T> 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<class... ArgTypes>
auto operator()(ArgTypes&&... args) &&
-> decltype(::std::invoke(::std::declval<rvalue_reference_wrapper<T>>().get(), ::std::forward<ArgTypes>(args)...))
{
return (::std::invoke(::std::move(*this).get(), ::std::forward<ArgTypes>(args)...));
}
private:
T* _pointer;
};
template<class T>
inline rvalue_reference_wrapper<T> rv_ref(T& ref_value) = delete;
template<class T>
inline ::std::enable_if_t<!(::std::is_lvalue_reference<T>::value), rvalue_reference_wrapper<T>> rv_ref(T&& ref_value) noexcept
{
return rvalue_reference_wrapper<T>(::std::forward<T>(ref_value));
}
#ifdef _MSC_VER
namespace std
{
template<class T>
struct _Unrefwrap_helper<rvalue_reference_wrapper<T>>
{
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<int>(42);
auto test_tuple = std::make_tuple(42, std::ref(a), rv_ref(std::move(p_int)));
static_assert(std::is_same<decltype(test_tuple), std::tuple<int, int &, std::unique_ptr<int>&&>>::value, "unexpected result");
我认为实施类似的&#34;展开&#34;并不难。其他标准库实现的逻辑。
答案 3 :(得分:0)
您可以使用可变的lambda对象。
auto func = [=]() mutable {
f(std::move(s));
};