是否有一个reference_wrapper<>对于右值引用?

发布于 2024-10-19 05:49:13 字数 1239 浏览 4 评论 0 原文

我想知道如何完成以下操作

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,在本例中。

I wonder how the following can be done

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

How can I pass an rvalue reference and store it as an rvalue reference (possibly wrapped) in the call wrapper? I know I can manually write up a class like std::reference_wrapper<> that has a conversion function to T&&, but I would rather want to avoid that and use Standard technology.


I implemented it like AProgrammer recommends:

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 {}; 
} 

Now I can say

void f(string &&s) { 
  std::string i(move(s)); 
  /* other stuff */ 
} 

int main() { 
  std::string s; 
  bind(f, make_adv(move(s)))(); // Works!
}

If we pass an lvalue to make_adv, it will forward it as an lvalue referring to the input argument, so it can be used as a replacement for std::ref, in this case.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

随遇而安 2024-10-26 05:49:13

我对此的看法。

N3225 中的 20.8.10.1.2/10

绑定参数 v1, v2, ..., vN 的值及其对应类型 V1, V2, ..., VN
取决于从调用 bind 派生的类型 TiD 和调用包装器 g 的 cv 限定符 cv 为
如下:

  • 如果TiD为reference_wrapper,则参数为tid.get(),Vi类型为T&
  • 如果 is_bind_expression::value 的值为 true,则参数为 tid(std::forward(uj)...)
    其类型 Vi 为 result_of::type;
  • 如果 is_placeholder::value 的值 j 不为零,则参数为 std::forward(uj)
    其类型 Vi 为 Uj&&;
  • 否则,值为 tid,其类型 Vi 为 TiD cv &。

因此,拥有右值引用的唯一可能是让 is_bind_expression::value true 或 is_placeholder::value 不为零。第二种可能性会产生您不想要的影响,并且通过第一种可能性实现想要的结果意味着,如果我们限制为标准提供的类型,我们试图解决的问题就得到了解决。因此,唯一的可能性是提供您自己的包装器和 is_bind_expression 的专门化(20.8.10.1.1/1 允许),因为我没有看到。

My take on this.

20.8.10.1.2/10 in N3225

The values of the bound arguments v1, v2, ..., vN and their corresponding types V1, V2, ..., VN
depend on the types TiD derived from the call to bind and the cv-qualifiers cv of the call wrapper g as
follows:

  • if TiD is reference_wrapper, the argument is tid.get() and its type Vi is T&;
  • if the value of is_bind_expression::value is true, the argument is tid(std::forward(uj)...)
    and its type Vi is result_of::type;
  • if the value j of is_placeholder::value is not zero, the argument is std::forward(uj)
    and its type Vi is Uj&&;
  • otherwise, the value is tid and its type Vi is TiD cv &.

So the only possibility to have a rvalue reference is to have is_bind_expression<TiD>::value true or is_placeholder<TiD>::value not zero. The second possibility has implications you don't want and achieving the wanted result with the first would imply that the problem we are trying to solve is solved if we restrict to the standard provided types. So, the only possibility would be to provide your own wrapper and a specialisation for is_bind_expression<TiD> (that is allowed by 20.8.10.1.1/1) as I don't see one.

七堇年 2024-10-26 05:49:13

如何传递右值引用并将其作为右值引用存储在调用包装器中?

这里的问题是这样的绑定函数对象可以被多次调用。如果函数对象将绑定参数作为右值转发,则显然只能工作一次。所以,这是一个安全问题。

但在某些情况下,这种转发正是您想要的。您可以使用 lambda 作为中介:

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

基本上,我想出了这个绑定+lambda 组合作为缺少“移动捕获”的解决方法。

How can I pass an rvalue reference and store it as an rvalue reference in the call wrapper?

The problem here is that such a bind function object can be invoked multiple times. If the function object forwarded a bound parameter as rvalue this would obviously only work once. So, this is a bit of a safety issue.

But in some cases this kind of forwarding is exactly what you want. You could use a lambda as an intermediary:

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

Basically, I came up with this bind+lambda combination as a workaround for a missing "move-capture".

瑶笙 2024-10-26 05:49:13

当我偶然发现这个问题时,我正在谷歌搜索“reference_wrapper for rvalues”。
不确定我的答案是否有用,它与 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");

我相信为其他标准实现类似的“展开”逻辑并不困难库的实现。

I was googling for "reference_wrapper for rvalues" when I stumbled on this question.
Not sure whether my answer would be useful, it is not related to std::bind and actually doesn't work with it, but for some other use cases it might help somebody.

Here's my attempt to implement 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

The last specialization in namespace std allows MSVC's implementation of standard library to work with my type, e.g. when using 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");

I believe it would not be hard to implement similar "unwrapping" logic for other standard library implementations.

风吹短裙飘 2024-10-26 05:49:13

您可以使用可变的 lambda 对象。

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

You can use a mutable lambda object.

auto func = [=]() mutable {
    f(std::move(s));
};
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文