繁体   English   中英

统一处理左值和右值引用

[英]Handling lvalue and rvalue references uniformly

我有两个相关的问题,都与如何统一处理左值和右值引用有关。

虚函数案例:

struct Foo {
    Object m_object;

    virtual void bar(SomeRefTypeOfObject object);
};

基本上,我要实现的是拥有SomeRefTypeOfObject ,它能够存储对Object左值和右值引用。 bar是一个较大的函数,它将使用一个m_object = object; 语句存储object的值(复制或移动操作,取决于存储的引用的类型)。 原因是我要避免具有两个bar函数(对于每种引用类型)。 标准库中是否有任何东西可以方便有效地做到这一点,或者我必须为此推出自己的解决方案? 如果我必须推出自己的解决方案,那么好的实现会是什么样子?

模板功能案例:

struct Foo {
    Object m_object;

    template <...>
    void bar(... object);
};

我想有一个bar模板,可以用任何类型的Object或其派生类/可以转换为Object其他对象(如bar是带有const Object &Object &&参数的两个重载函数)来调用它,但仅使用 const Object &Object &&实例化。 因此,我不想为每个派生类型实例化bar 最清楚的方法是什么? 我想我在这里需要某种形式的SFINAE。

注意: m_object = object; 赋值可以在bar调用的函数中进行。 因此,按值传递对象的解决方案不是最佳的,因为将进行不必要的复制/移动(可能存在类型的移动不如传递引用便宜)。 此外,赋值可以有条件地发生,因此仅在叶子函数中按值传递对象不是解决方案(因为它可能与运行时相关,因此哪个函数进行实际赋值)。

该方法的类型擦除(或参考擦除可能是更好的术语):

#include <iostream>
#include <type_traits>
#include <utility>

struct Object { 
    Object() {
        std::cout << "Object()" << std::endl;
    }
    Object(const Object &) {
        std::cout << "Object(const Object &)" << std::endl;
    }
    Object(Object &&) {
        std::cout << "Object(Object &&)" << std::endl;
    }
    Object &operator =(const Object &) {
        std::cout << "Object &operator =(const Object &)" << std::endl;
        return *this;
    }
    Object &operator =(Object &&) {
        std::cout << "Object &operator =(Object &&)" << std::endl;
        return *this;
    }
};

template <class T>
struct RLReferenceWrapper {
    bool is_rvalue;
    T *pnull;
    const T& cref;
    T&& rref;
    RLReferenceWrapper(T&& rref): is_rvalue(true), pnull(nullptr), cref(*pnull), rref(std::move(rref)) { }
    RLReferenceWrapper(const T& cref): is_rvalue(false), pnull(nullptr), cref(cref), rref(std::move(*pnull)) { }

    template <class L>
    void Visit(L l) {
        if (is_rvalue) {
            l(std::move(rref));
        } else {
            l(cref);
        }
    }
};

struct Foo {
    Object m_object;

    virtual void bar(RLReferenceWrapper<Object> wrapper) {
        wrapper.Visit([this](auto&& ref){ 
            m_object = std::forward<std::remove_reference_t<decltype(ref)>>(ref);
        });
    }
};

int main() {
    Foo f;
    f.bar(Object{});
    Object o2;
    f.bar(o2);
}

输出:

Object()
Object()
Object &operator =(Object &&)
Object()
Object &operator =(const Object &)

[现场演示]

替代解决方案可能是(未经测试):

template <class T> struct movable{};
template <class T> struct movable<T const&>
{
  movable( movable const& ) = delete;
  movable( T const& r ): ref_{ const_cast<T&>(r) }, movable_{false} {}
  movable( T&& r ): ref_{r}, movable_{true} {}

  auto get() const -> T const& { return ref_; }
  auto move() -> T&& { if( !movable_ ) throw std::runtime_error{"bad move!"}; movable_ = false; return std::move(ref_); }

  template <class V>
  void assign_or_move_to( V& v ) { if( movable_ ) v = move(); else v = get(); }

private:
  T&   ref_;
  bool movable_;
};

struct Foo {
  Object m_object;

  virtual void bar( movable<Object const&> object_ref )
  {
    auto& object = object_ref.get();

    // ...

    object_ref.assign_or_move_to( m_object );

    // or

    if( something ) m_object = object_ref.move();
  }
};

就是说,我怀疑是否值得,您仍然应该向我们解释为什么您拒绝编写两个开始的bar重载...

怎么样

struct Foo {
    Object m_object;

    void bar(Object& o) { bar_impl(o); }
    void bar(Object&& o) { bar_impl(std::move(o)); }

    template <typename T>
    void bar_impl(T&& object) { 
        // snip ...
        m_object = std::forward<T>(object);
    }
};

我认为这:

  1. 仅实例化Object&和Object &&的栏
  2. 只有一个long函数实例
  3. 允许模板根据传递的内容来选择复制还是移动

它没有包装对象或其他任何东西,但我认为没关系b / c T&&不是这样的右值,它是通用参考,如此处所述: https : //isocpp.org/blog/2012/11/universal -引用c11-scott-meyers (感谢Scott!)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM